home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / bin / system-config-printer < prev    next >
Encoding:
Text File  |  2010-09-28  |  281.3 KB  |  7,080 lines

  1. #!/usr/bin/python
  2.  
  3. ## system-config-printer
  4.  
  5. ## Copyright (C) 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
  6. ## Authors:
  7. ##  Tim Waugh <twaugh@redhat.com>
  8. ##  Florian Festi <ffesti@redhat.com>
  9.  
  10. ## This program is free software; you can redistribute it and/or modify
  11. ## it under the terms of the GNU General Public License as published by
  12. ## the Free Software Foundation; either version 2 of the License, or
  13. ## (at your option) any later version.
  14.  
  15. ## This program is distributed in the hope that it will be useful,
  16. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. ## GNU General Public License for more details.
  19.  
  20. ## You should have received a copy of the GNU General Public License
  21. ## along with this program; if not, write to the Free Software
  22. ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. import sys
  25. sys.path.append("/usr/share/system-config-printer")
  26. # config is generated from config.py.in by configure
  27. import config
  28.  
  29. import errno
  30. import sys, os, tempfile, time, traceback, re, httplib, glob
  31. import subprocess
  32. import signal, thread
  33. from timedops import *
  34. import dbus
  35. try:
  36.     import gtk
  37. except RuntimeError, e:
  38.     print "system-config-printer:", e
  39.     print "This is a graphical application and requires DISPLAY to be set."
  40.     sys.exit (1)
  41.  
  42. import glib
  43. def show_uri (uri):
  44.     gtk.show_uri (gtk.gdk.screen_get_default (),
  45.                   uri,
  46.                   gtk.get_current_event_time ())
  47.  
  48. gtk.about_dialog_set_url_hook (lambda x, y: show_uri (y))
  49. gtk.about_dialog_set_email_hook (lambda x, y: show_uri ("mailto:" + y))
  50.  
  51. def show_help():
  52.     print ("\nThis is system-config-printer, " \
  53.            "a CUPS server configuration program.\n\n"
  54.            "Options:\n\n"
  55.            "  --setup-printer URI\n"
  56.            "            Select the (detected) CUPS device URI on start-up,\n"
  57.            "            and run the new-printer wizard for it.\n\n"
  58.            "  --configure-printer NAME\n"
  59.            "            Select the named printer on start-up, and open its\n"
  60.            "            properties dialog.\n\n"
  61.            "  --choose-driver NAME\n"
  62.            "            Select the named printer on start-up, and display\n"
  63.            "            the list of drivers.\n\n"
  64.            "  --print-test-page NAME\n"
  65.            "            Select the named printer on start-up and print a\n"
  66.            "            test page to it.\n\n"
  67.            "  --no-focus-on-map\n"
  68.            "            Do not focus the main window, to prevent focus \n"
  69.            "            stealing\n\n"
  70.            "  --debug   Enable debugging output.\n")
  71.  
  72. if len(sys.argv)>1 and sys.argv[1] == '--help':
  73.     show_help ()
  74.     sys.exit (0)
  75.  
  76. import cups
  77. cups.require ("1.9.46")
  78. cups.ppdSetConformance (cups.PPD_CONFORM_RELAXED)
  79.  
  80. try:
  81.     import pysmb
  82.     PYSMB_AVAILABLE=True
  83. except:
  84.     PYSMB_AVAILABLE=False
  85.  
  86. import cupshelpers, options
  87. import gobject # for TYPE_STRING and TYPE_PYOBJECT
  88. from gui import GtkGUI
  89. from optionwidgets import OptionWidget
  90. from debug import *
  91. import probe_printer
  92. import gtk_label_autowrap
  93. import urllib
  94. import troubleshoot
  95. import jobviewer
  96. import authconn
  97. import monitor
  98. from smburi import SMBURI
  99. import errordialogs
  100. from errordialogs import *
  101. import installpackage
  102. import userdefault
  103. from AdvancedServerSettings import AdvancedServerSettings
  104. from PhysicalDevice import PhysicalDevice
  105. from ToolbarSearchEntry import *
  106. from GroupsPane import *
  107. from GroupsPaneModel import *
  108. from SearchCriterion import *
  109. import gtkinklevel
  110. import gtkspinner
  111. import statereason
  112. import firewall
  113. import asyncconn
  114. import ppdsloader
  115. import dnssdresolve
  116.  
  117. domain='system-config-printer'
  118. import locale
  119. try:
  120.     locale.setlocale (locale.LC_ALL, "")
  121. except locale.Error:
  122.     os.environ['LC_ALL'] = 'C'
  123.     locale.setlocale (locale.LC_ALL, "")
  124. from gettext import gettext as _
  125. monitor.set_gettext_function (_)
  126. errordialogs.set_gettext_function (_)
  127. asyncconn.set_gettext_function (_)
  128. authconn.set_gettext_function (_)
  129. ppdsloader.set_gettext_function (_)
  130. import gettext
  131. gettext.textdomain (domain)
  132. gettext.bindtextdomain (domain, config.localedir)
  133. import ppdippstr
  134. ppdippstr.init ()
  135. pkgdata = config.pkgdatadir
  136. iconpath = os.path.join (pkgdata, 'icons/')
  137. sys.path.append (pkgdata)
  138.  
  139. busy_cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
  140.  
  141. TEXT_start_firewall_tool = _("To do this, select "
  142.                              "System->Administration->Firewall "
  143.                              "from the main menu.")
  144.  
  145. try:
  146.     try_CUPS_SERVER_REMOTE_ANY = cups.CUPS_SERVER_REMOTE_ANY
  147. except AttributeError:
  148.     # cups module was compiled with CUPS < 1.3
  149.     try_CUPS_SERVER_REMOTE_ANY = "_remote_any"
  150.  
  151. def validDeviceURI (uri):
  152.     """Returns True is the provided URI is valid."""
  153.     (scheme, rest) = urllib.splittype (uri)
  154.     if scheme == None or scheme == '':
  155.         return False
  156.     return True
  157.  
  158. def CUPS_server_hostname ():
  159.     host = cups.getServer ()
  160.     if host[0] == '/':
  161.         return 'localhost'
  162.     return host
  163.  
  164. # Both the printer properties window and the new printer window
  165. # need to be able to drive 'class members' selections.
  166. def moveClassMembers(treeview_from, treeview_to):
  167.     selection = treeview_from.get_selection()
  168.     model_from, rows = selection.get_selected_rows()
  169.     rows = [gtk.TreeRowReference(model_from, row) for row in rows]
  170.  
  171.     model_to = treeview_to.get_model()
  172.  
  173.     for row in rows:
  174.         path = row.get_path()
  175.         iter = model_from.get_iter(path)
  176.         row_data = model_from.get(iter, 0)
  177.         model_to.append(row_data)
  178.         model_from.remove(iter)
  179.  
  180. def getCurrentClassMembers(treeview):
  181.     model = treeview.get_model()
  182.     iter = model.get_iter_root()
  183.     result = []
  184.     while iter:
  185.         result.append(model.get(iter, 0)[0])
  186.         iter = model.iter_next(iter)
  187.     result.sort()
  188.     return result
  189.  
  190. def on_delete_just_hide (widget, event):
  191.     widget.hide ()
  192.     return True # stop other handlers
  193.  
  194. class GUI(GtkGUI, monitor.Watcher):
  195.  
  196.     printer_states = { cups.IPP_PRINTER_IDLE: _("Idle"),
  197.                        cups.IPP_PRINTER_PROCESSING: _("Processing"),
  198.                        cups.IPP_PRINTER_BUSY: _("Busy"),
  199.                        cups.IPP_PRINTER_STOPPED: _("Stopped") }
  200.  
  201.     def __init__(self, setup_printer = None, configure_printer = None,
  202.                  change_ppd = False, devid = "", print_test_page = False,
  203.                  focus_on_map = True):
  204.  
  205.         try:
  206.             self.language = locale.getlocale(locale.LC_MESSAGES)
  207.             self.encoding = locale.getlocale(locale.LC_CTYPE)
  208.         except:
  209.             nonfatalException()
  210.             os.environ['LC_ALL'] = 'C'
  211.             locale.setlocale (locale.LC_ALL, "")
  212.             self.language = locale.getlocale(locale.LC_MESSAGES)
  213.             self.encoding = locale.getlocale(locale.LC_CTYPE)
  214.  
  215.         self.printer = self.ppd = None
  216.         self.printers = {}
  217.         self.conflicts = set() # of options
  218.         self.connect_server = (self.printer and self.printer.getServer()) \
  219.                                or cups.getServer()
  220.         self.connect_encrypt = cups.getEncryption ()
  221.         self.connect_user = cups.getUser()
  222.  
  223.         self.changed = set() # of options
  224.  
  225.         self.servers = set((self.connect_server,))
  226.         self.server_is_publishing = None # not known
  227.         self.devid = devid
  228.         self.focus_on_map = focus_on_map
  229.  
  230.         # WIDGETS
  231.         # =======
  232.         self.updating_widgets = False
  233.         self.getWidgets({"PrintersWindow":
  234.                              ["PrintersWindow",
  235.                               "view_area_vbox",
  236.                               "view_area_scrolledwindow",
  237.                               "dests_iconview",
  238.                               "statusbarMain",
  239.                               "toolbar",
  240.                               "server_menubar_item",
  241.                               "group_menubar_item",
  242.                               "printer_menubar_item",
  243.                               "view_discovered_printers",
  244.                               "view_groups"],
  245.                          "AboutDialog":
  246.                              ["AboutDialog"],
  247.                          "ConnectDialog":
  248.                              ["ConnectDialog",
  249.                               "chkEncrypted",
  250.                               "cmbServername",
  251.                               "btnConnect"],
  252.                          "ConnectingDialog":
  253.                              ["ConnectingDialog",
  254.                               "lblConnecting",
  255.                               "pbarConnecting"],
  256.                          "NewPrinterName":
  257.                              ["NewPrinterName",
  258.                               "entDuplicateName",
  259.                               "btnDuplicateOk"],
  260.                          "ServerSettingsDialog":
  261.                              ["ServerSettingsDialog",
  262.                               "chkServerBrowse",
  263.                               "chkServerShare",
  264.                               "chkServerShareAny",
  265.                               "chkServerRemoteAdmin",
  266.                               "chkServerAllowCancelAll",
  267.                               "chkServerLogDebug",
  268.                               "hboxServerBrowse",
  269.                               "rbPreserveJobFiles",
  270.                               "rbPreserveJobHistory",
  271.                               "rbPreserveJobNone",
  272.                               "tvBrowseServers",
  273.                               "frameBrowseServers",
  274.                               "btAdvServerAdd",
  275.                               "btAdvServerRemove"],
  276.                          "PrinterPropertiesDialog":
  277.                              ["PrinterPropertiesDialog",
  278.                               "tvPrinterProperties",
  279.                               "btnPrinterPropertiesCancel",
  280.                               "btnPrinterPropertiesOK",
  281.                               "btnPrinterPropertiesApply",
  282.                               "btnPrinterPropertiesClose",
  283.                               "ntbkPrinter",
  284.                               "entPDescription",
  285.                               "entPLocation",
  286.                               "lblPMakeModel",
  287.                               "lblPMakeModel2",
  288.                               "lblPState",
  289.                               "entPDevice",
  290.                               "lblPDevice2",
  291.                               "btnSelectDevice",
  292.                               "btnChangePPD",
  293.                               "chkPEnabled",
  294.                               "chkPAccepting",
  295.                               "chkPShared",
  296.                               "lblNotPublished",
  297.                               "btnPrintTestPage",
  298.                               "btnSelfTest",
  299.                               "btnCleanHeads",
  300.                               "btnConflict",
  301.  
  302.                               "cmbPStartBanner",
  303.                               "cmbPEndBanner",
  304.                               "cmbPErrorPolicy",
  305.                               "cmbPOperationPolicy",
  306.  
  307.                               "rbtnPAllow",
  308.                               "rbtnPDeny",
  309.                               "tvPUsers",
  310.                               "entPUser",
  311.                               "btnPAddUser",
  312.                               "btnPDelUser",
  313.  
  314.                               "lblPInstallOptions",
  315.                               "swPInstallOptions",
  316.                               "vbPInstallOptions",
  317.                               "swPOptions",
  318.                               "lblPOptions",
  319.                               "vbPOptions",
  320.                               "algnClassMembers",
  321.                               "vbClassMembers",
  322.                               "lblClassMembers",
  323.                               "tvClassMembers",
  324.                               "tvClassNotMembers",
  325.                               "btnClassAddMember",
  326.                               "btnClassDelMember",
  327.                               "btnRefreshMarkerLevels",
  328.                               "tvPrinterStateReasons",
  329.                               "ntbkPrinterStateReasons",
  330.  
  331.                               # Job options
  332.                               "sbJOCopies", "btnJOResetCopies",
  333.                               "cmbJOOrientationRequested", "btnJOResetOrientationRequested",
  334.                               "cbJOFitplot", "btnJOResetFitplot",
  335.                               "cmbJONumberUp", "btnJOResetNumberUp",
  336.                               "cmbJONumberUpLayout", "btnJOResetNumberUpLayout",
  337.                               "sbJOBrightness", "btnJOResetBrightness",
  338.                               "cmbJOFinishings", "btnJOResetFinishings",
  339.                               "sbJOJobPriority", "btnJOResetJobPriority",
  340.                               "cmbJOMedia", "btnJOResetMedia",
  341.                               "cmbJOSides", "btnJOResetSides",
  342.                               "cmbJOHoldUntil", "btnJOResetHoldUntil",
  343.                               "cmbJOOutputOrder", "btnJOResetOutputOrder",
  344.                               "cbJOMirror", "btnJOResetMirror",
  345.                               "sbJOScaling", "btnJOResetScaling",
  346.                               "sbJOSaturation", "btnJOResetSaturation",
  347.                               "sbJOHue", "btnJOResetHue",
  348.                               "sbJOGamma", "btnJOResetGamma",
  349.                               "sbJOCpi", "btnJOResetCpi",
  350.                               "sbJOLpi", "btnJOResetLpi",
  351.                               "sbJOPageLeft", "btnJOResetPageLeft",
  352.                               "sbJOPageRight", "btnJOResetPageRight",
  353.                               "sbJOPageTop", "btnJOResetPageTop",
  354.                               "sbJOPageBottom", "btnJOResetPageBottom",
  355.                               "cbJOPrettyPrint", "btnJOResetPrettyPrint",
  356.                               "cbJOWrap", "btnJOResetWrap",
  357.                               "sbJOColumns", "btnJOResetColumns",
  358.                               "tblJOOther",
  359.                               "entNewJobOption", "btnNewJobOption",
  360.  
  361.                               # Marker levels
  362.                               "vboxMarkerLevels",
  363.                               "btnRefreshMarkerLevels"]},
  364.  
  365.                         domain=domain)
  366.  
  367.  
  368.         # Ensure the default PrintersWindow is shown despite
  369.         # the --no-focus-on-map option
  370.         self.PrintersWindow.set_focus_on_map (self.focus_on_map)
  371.  
  372.         # Since some dialogs are reused we can't let the delete-event's
  373.         # default handler destroy them
  374.         for dialog in [self.PrinterPropertiesDialog,
  375.                        self.ServerSettingsDialog]:
  376.             dialog.connect ("delete-event", on_delete_just_hide)
  377.  
  378.         self.ConnectingDialog.connect ("delete-event",
  379.                                        self.on_connectingdialog_delete)
  380.  
  381.         gtk.window_set_default_icon_name ('printer')
  382.  
  383.         # Printer Actions
  384.         printer_manager_action_group = \
  385.             gtk.ActionGroup ("PrinterManagerActionGroup")
  386.         printer_manager_action_group.add_actions ([
  387.                 ("connect-to-server", gtk.STOCK_CONNECT, _("_Connect..."),
  388.                  None, _("Choose a different CUPS server"),
  389.                  self.on_connect_activate),
  390.                 ("server-settings", gtk.STOCK_PREFERENCES, _("_Settings..."),
  391.                  None, _("Adjust server settings"),
  392.                  self.on_server_settings_activate),
  393.                 ("new-printer", gtk.STOCK_PRINT, _("_Printer"),
  394.                  None, None, self.on_new_printer_activate),
  395.                 ("new-class", gtk.STOCK_DND_MULTIPLE, _("_Class"),
  396.                  None, None, self.on_new_class_activate),
  397.                 ("quit", gtk.STOCK_QUIT, None, None, None,
  398.                  self.on_quit_activate)])
  399.         printer_manager_action_group.add_actions ([
  400.                 ("rename-printer", None, _("_Rename"),
  401.                  None, None, self.on_rename_activate),
  402.                 ("duplicate-printer", gtk.STOCK_COPY, _("_Duplicate"),
  403.                  "<Ctrl>d", None, self.on_duplicate_activate),
  404.                 ("delete-printer", gtk.STOCK_DELETE, None,
  405.                  None, None, self.on_delete_activate),
  406.                 ("set-default-printer", gtk.STOCK_HOME, _("Set As De_fault"),
  407.                  None, None, self.on_set_as_default_activate),
  408.                 ("edit-printer", gtk.STOCK_PROPERTIES, None,
  409.                  None, None, self.on_edit_activate),
  410.                 ("create-class", gtk.STOCK_DND_MULTIPLE, _("_Create class"),
  411.                  None, None, self.on_create_class_activate),
  412.                 ("view-print-queue", gtk.STOCK_FIND, _("View Print _Queue"),
  413.                  None, None, self.on_view_print_queue_activate),
  414.                 ("add-to-group", None, _("_Add to Group"),
  415.                  None, None, None),
  416.                 ("save-as-group", None, _("Save Results as _Group"),
  417.                  None, None, self.on_save_as_group_activate),
  418.                 ("save-as-search-group", None, _("Save Filter as _Search Group"),
  419.                  None, None, self.on_save_as_search_group_activate),
  420.                 ])
  421.         printer_manager_action_group.add_toggle_actions ([
  422.                 ("enable-printer", None, _("E_nabled"),
  423.                  None, None, self.on_enabled_activate),
  424.                 ("share-printer", None, _("_Shared"),
  425.                  None, None, self.on_shared_activate),
  426.                 ])
  427.         printer_manager_action_group.add_radio_actions ([
  428.                 ("filter-name", None, _("Name")),
  429.                 ("filter-description", None, _("Description")),
  430.                 ("filter-location", None, _("Location")),
  431.                 ("filter-manufacturer", None, _("Manufacturer / Model")),
  432.                 ], 1, self.on_filter_criterion_changed)
  433.         for action in printer_manager_action_group.list_actions ():
  434.             action.set_sensitive (False)
  435.         for action in ["connect-to-server",
  436.                        "quit",
  437.                        "view-print-queue",
  438.                        "filter-name",
  439.                        "filter-description",
  440.                        "filter-location",
  441.                        "filter-manufacturer"]:
  442.             act = printer_manager_action_group.get_action (action)
  443.             act.set_sensitive (True)
  444.  
  445.         self.ui_manager = gtk.UIManager ()
  446.         self.ui_manager.insert_action_group (printer_manager_action_group, -1)
  447.         self.ui_manager.add_ui_from_string (
  448. """
  449. <ui>
  450.  <accelerator action="connect-to-server"/>
  451.  <accelerator action="server-settings"/>
  452.  <accelerator action="new-printer"/>
  453.  <accelerator action="new-class"/>
  454.  <accelerator action="quit"/>
  455.  
  456.  <accelerator action="rename-printer"/>
  457.  <accelerator action="duplicate-printer"/>
  458.  <accelerator action="delete-printer"/>
  459.  <accelerator action="set-default-printer"/>
  460.  <accelerator action="edit-printer"/>
  461.  <accelerator action="create-class"/>
  462.  <accelerator action="view-print-queue"/>
  463.  <accelerator action="add-to-group"/>
  464.  <accelerator action="save-as-group"/>
  465.  <accelerator action="save-as-search-group"/>
  466.  <accelerator action="enable-printer"/>
  467.  <accelerator action="share-printer"/>
  468.  <accelerator action="filter-name"/>
  469.  <accelerator action="filter-description"/>
  470.  <accelerator action="filter-location"/>
  471.  <accelerator action="filter-manufacturer"/>
  472. </ui>
  473. """
  474. )
  475.         self.ui_manager.ensure_update ()
  476.         self.PrintersWindow.add_accel_group (self.ui_manager.get_accel_group ())
  477.  
  478.         # Toolbar
  479.         # Glade-2 doesn't have support for MenuToolButton, so we do that here.
  480.         self.btnNew = gtk.MenuToolButton (gtk.STOCK_ADD)
  481.         self.btnNew.set_is_important (True)
  482.         newmenu = gtk.Menu ()
  483.         action = self.ui_manager.get_action ("/new-printer")
  484.         newprinteritem = action.create_menu_item ()
  485.         action = self.ui_manager.get_action ("/new-class")
  486.         newclassitem = action.create_menu_item ()
  487.         newprinteritem.show ()
  488.         newclassitem.show ()
  489.         newmenu.attach (newprinteritem, 0, 1, 0, 1)
  490.         newmenu.attach (newclassitem, 0, 1, 1, 2)
  491.         self.btnNew.set_menu (newmenu)
  492.         self.toolbar.add (self.btnNew)
  493.         self.toolbar.add (gtk.SeparatorToolItem ())
  494.         refreshbutton = gtk.ToolButton (gtk.STOCK_REFRESH)
  495.         refreshbutton.connect ('clicked', self.on_btnRefresh_clicked)
  496.         self.toolbar.add (refreshbutton)
  497.         self.toolbar.show_all ()
  498.  
  499.         server_context_menu = gtk.Menu ()
  500.         for action_name in ["connect-to-server",
  501.                             "server-settings",
  502.                             None,
  503.                             "new",
  504.                             None,
  505.                             "quit"]:
  506.             if action_name == "new":
  507.                 item = gtk.MenuItem (_("_New"))
  508.                 item.set_sensitive (True)
  509.                 self.menuItemNew = item
  510.             elif not action_name:
  511.                 item = gtk.SeparatorMenuItem ()
  512.             else:
  513.                 action = printer_manager_action_group.get_action (action_name)
  514.                 item = action.create_menu_item ()
  515.             item.show ()
  516.             server_context_menu.append (item)
  517.         self.server_menubar_item.set_submenu (server_context_menu)
  518.  
  519.         new_menu = gtk.Menu ()
  520.         for action_name in ["new-printer",
  521.                             "new-class"]:
  522.             action = printer_manager_action_group.get_action (action_name)
  523.             item = action.create_menu_item ()
  524.             item.show ()
  525.             new_menu.append (item)
  526.         self.menuItemNew.set_submenu (new_menu)
  527.  
  528.         self.printer_context_menu = gtk.Menu ()
  529.         for action_name in ["edit-printer",
  530.                             "duplicate-printer",
  531.                             "rename-printer",
  532.                             "delete-printer",
  533.                             None,
  534.                             "enable-printer",
  535.                             "share-printer",
  536.                             "create-class",
  537.                             "set-default-printer",
  538.                             None,
  539.                             "add-to-group",
  540.                             "view-print-queue"]:
  541.             if not action_name:
  542.                 item = gtk.SeparatorMenuItem ()
  543.             else:
  544.                 action = printer_manager_action_group.get_action (action_name)
  545.                 item = action.create_menu_item ()
  546.             item.show ()
  547.             self.printer_context_menu.append (item)
  548.         self.printer_menubar_item.set_submenu (self.printer_context_menu)
  549.  
  550.         self.jobviewers = [] # to keep track of jobviewer windows
  551.  
  552.         # Printer properties combo boxes
  553.         for combobox in [self.cmbPStartBanner,
  554.                          self.cmbPEndBanner,
  555.                          self.cmbPErrorPolicy,
  556.                          self.cmbPOperationPolicy]:
  557.             cell = gtk.CellRendererText ()
  558.             combobox.clear ()
  559.             combobox.pack_start (cell, True)
  560.             combobox.add_attribute (cell, 'text', 0)
  561.  
  562.         btn = self.btnRefreshMarkerLevels
  563.         btn.connect ("clicked", self.on_btnRefreshMarkerLevels_clicked)
  564.  
  565.         # Printer state reasons list
  566.         column = gtk.TreeViewColumn (_("Message"))
  567.         icon = gtk.CellRendererPixbuf ()
  568.         column.pack_start (icon, False)
  569.         text = gtk.CellRendererText ()
  570.         column.pack_start (text, False)
  571.         column.set_cell_data_func (icon, self.set_printer_state_reason_icon)
  572.         column.set_cell_data_func (text, self.set_printer_state_reason_text)
  573.         column.set_resizable (True)
  574.         self.tvPrinterStateReasons.append_column (column)
  575.         selection = self.tvPrinterStateReasons.get_selection ()
  576.         selection.set_mode (gtk.SELECTION_NONE)
  577.         store = gtk.ListStore (int, str)
  578.         self.tvPrinterStateReasons.set_model (store)
  579.  
  580.         # New Printer Dialog
  581.         self.newPrinterGUI = np = NewPrinterGUI(self)
  582.  
  583.         # Set up "About" dialog
  584.         self.AboutDialog.set_program_name(domain)
  585.         self.AboutDialog.set_version(config.VERSION)
  586.         self.AboutDialog.set_icon_name('printer')
  587.  
  588.         # Set up "Problems?" link button
  589.         class UnobtrusiveButton(gtk.Button):
  590.             def __init__ (self, **args):
  591.                 gtk.Button.__init__ (self, **args)
  592.                 self.set_relief (gtk.RELIEF_NONE)
  593.                 label = self.get_child ()
  594.                 text = label.get_text ()
  595.                 label.set_use_markup (True)
  596.                 label.set_markup ('<span size="small" ' +
  597.                                   'underline="single" ' +
  598.                                   'color="#0000ee">%s</span>' % text)
  599.  
  600.         problems = UnobtrusiveButton (label=_("Problems?"))
  601.         self.hboxServerBrowse.pack_end (problems, False, False, 0)
  602.         problems.connect ('clicked', self.on_problems_button_clicked)
  603.         problems.show ()
  604.  
  605.         self.static_tabs = 3
  606.  
  607.         gtk_label_autowrap.set_autowrap(self.PrintersWindow)
  608.  
  609.         try:
  610.             self.cups = authconn.Connection(self.PrintersWindow)
  611.         except RuntimeError:
  612.             self.cups = None
  613.  
  614.         self.status_context_id = self.statusbarMain.get_context_id(
  615.             "Connection")
  616.         self.setConnected()
  617.  
  618.         # Setup search and printer groups
  619.         self.setup_toolbar_for_search_entry ()
  620.         self.current_filter_text = ""
  621.         self.current_filter_mode = "filter-name"
  622.  
  623.         self.groups_pane = GroupsPane ()
  624.         self.current_groups_pane_item = self.groups_pane.get_selected_item ()
  625.         self.groups_pane.connect ('item-activated',
  626.                                   self.on_groups_pane_item_activated)
  627.         self.groups_pane.connect ('items-changed',
  628.                                   self.on_groups_pane_items_changed)
  629.         self.PrintersWindow.add_accel_group (
  630.             self.groups_pane.ui_manager.get_accel_group ())
  631.         self.view_area_hpaned = gtk.HPaned ()
  632.         self.view_area_hpaned.add1 (self.groups_pane)
  633.         self.groups_pane_visible = False
  634.         if self.groups_pane.n_groups () > 0:
  635.             self.view_groups.set_active (True)
  636.  
  637.         # Group menubar item
  638.         self.group_menubar_item.set_submenu (self.groups_pane.groups_menu)
  639.  
  640.         # "Add to Group" submenu
  641.         self.add_to_group_menu = gtk.Menu ()
  642.         self.update_add_to_group_menu ()
  643.         action = printer_manager_action_group.get_action ("add-to-group")
  644.         for proxy in action.get_proxies ():
  645.             if isinstance (proxy, gtk.MenuItem):
  646.                 item = proxy
  647.                 break
  648.         item.set_submenu (self.add_to_group_menu)
  649.  
  650.         # Search entry drop down menu
  651.         menu = gtk.Menu ()
  652.         for action_name in ["filter-name",
  653.                             "filter-description",
  654.                             "filter-location",
  655.                             "filter-manufacturer",
  656.                             None,
  657.                             "save-as-group",
  658.                             "save-as-search-group"]:
  659.             if not action_name:
  660.                 item = gtk.SeparatorMenuItem ()
  661.             else:
  662.                 action = printer_manager_action_group.get_action (action_name)
  663.                 item = action.create_menu_item ()
  664.             menu.append (item)
  665.         menu.show_all ()
  666.         self.search_entry.set_drop_down_menu (menu)
  667.  
  668.         # Setup icon view
  669.         self.mainlist = gtk.ListStore(gobject.TYPE_PYOBJECT, # Object
  670.                                       gtk.gdk.Pixbuf,        # Pixbuf
  671.                                       gobject.TYPE_STRING,   # Name
  672.                                       gobject.TYPE_STRING)   # Tooltip
  673.  
  674.         self.dests_iconview.set_model(self.mainlist)
  675.         self.dests_iconview.set_column_spacing (30)
  676.         self.dests_iconview.set_row_spacing (20)
  677.         self.dests_iconview.set_pixbuf_column (1)
  678.         self.dests_iconview.set_text_column (2)
  679.         self.dests_iconview.set_tooltip_column (3)
  680.         self.dests_iconview.set_has_tooltip(True)
  681.         self.dests_iconview.connect ('key-press-event',
  682.                                      self.dests_iconview_key_press_event)
  683.         self.dests_iconview.connect ('item-activated',
  684.                                      self.dests_iconview_item_activated)
  685.         self.dests_iconview.connect ('selection-changed',
  686.                                      self.dests_iconview_selection_changed)
  687.         self.dests_iconview.connect ('button-press-event',
  688.                                      self.dests_iconview_button_press_event)
  689.         self.dests_iconview.connect ('popup-menu',
  690.                                      self.dests_iconview_popup_menu)
  691.         self.dests_iconview_selection_changed (self.dests_iconview)
  692.         self.dests_iconview.enable_model_drag_source (gtk.gdk.BUTTON1_MASK,
  693.                                                       # should use a variable
  694.                                                       # instead of 0
  695.                                                       [("queue", 0, 0)],
  696.                                                       gtk.gdk.ACTION_COPY)
  697.         self.dests_iconview.connect ("drag-data-get",
  698.                                      self.dests_iconview_drag_data_get)
  699.  
  700.         # setup some lists
  701.         m = gtk.SELECTION_MULTIPLE
  702.         s = gtk.SELECTION_SINGLE
  703.         b = gtk.SELECTION_BROWSE
  704.         for name, treeview, selection_mode in (
  705.             (_("Members of this class"), self.tvClassMembers, m),
  706.             (_("Others"), self.tvClassNotMembers, m),
  707.             (_("Members of this class"), np.tvNCMembers, m),
  708.             (_("Others"), np.tvNCNotMembers, m),
  709.             (_("Devices"), np.tvNPDevices, s),
  710.             (_("Connections"), np.tvNPDeviceURIs, s),
  711.             (_("Makes"), np.tvNPMakes,s),
  712.             (_("Models"), np.tvNPModels,s),
  713.             (_("Drivers"), np.tvNPDrivers,s),
  714.             (_("Downloadable Drivers"), np.tvNPDownloadableDrivers, b),
  715.             (_("Users"), self.tvPUsers, m),
  716.             ):
  717.  
  718.             model = gtk.ListStore(str)
  719.             cell = gtk.CellRendererText()
  720.             column = gtk.TreeViewColumn(name, cell, text=0)
  721.             treeview.set_model(model)
  722.             treeview.append_column(column)
  723.             treeview.get_selection().set_mode(selection_mode)
  724.  
  725.         # Server Settings dialog
  726.         self.ServerSettingsDialog.connect ('response',
  727.                                            self.server_settings_response)
  728.  
  729.         # Printer Properties dialog
  730.         self.PrinterPropertiesDialog.connect ('response',
  731.                                               self.printer_properties_response)
  732.  
  733.         # Printer Properties tree view
  734.         col = gtk.TreeViewColumn ('', gtk.CellRendererText (), markup=0)
  735.         self.tvPrinterProperties.append_column (col)
  736.         sel = self.tvPrinterProperties.get_selection ()
  737.         sel.connect ('changed', self.on_tvPrinterProperties_selection_changed)
  738.         sel.set_mode (gtk.SELECTION_SINGLE)
  739.  
  740.         # Job Options widgets.
  741.         for (widget,
  742.              opts) in [(self.cmbJOOrientationRequested,
  743.                         [[_("Portrait (no rotation)")],
  744.                          [_("Landscape (90 degrees)")],
  745.                          [_("Reverse landscape (270 degrees)")],
  746.                          [_("Reverse portrait (180 degrees)")]]),
  747.  
  748.                        (self.cmbJONumberUp,
  749.                         [["1"], ["2"], ["4"], ["6"], ["9"], ["16"]]),
  750.  
  751.                        (self.cmbJONumberUpLayout,
  752.                         [[_("Left to right, top to bottom")],
  753.                          [_("Left to right, bottom to top")],
  754.                          [_("Right to left, top to bottom")],
  755.                          [_("Right to left, bottom to top")],
  756.                          [_("Top to bottom, left to right")],
  757.                          [_("Top to bottom, right to left")],
  758.                          [_("Bottom to top, left to right")],
  759.                          [_("Bottom to top, right to left")]]),
  760.  
  761.                        (self.cmbJOFinishings,
  762.   # See section 4.2.6 of this document for explanation of finishing types:
  763.   # ftp://ftp.pwg.org/pub/pwg/candidates/cs-ippfinishings10-20010205-5100.1.pdf
  764.                         [[_("None")],
  765.                          [_("Staple")],
  766.                          [_("Punch")],
  767.                          [_("Cover")],
  768.                          [_("Bind")],
  769.                          [_("Saddle stitch")],
  770.                          [_("Edge stitch")],
  771.                          [_("Fold")],
  772.                          [_("Trim")],
  773.                          [_("Bale")],
  774.                          [_("Booklet maker")],
  775.                          [_("Job offset")],
  776.                          [_("Staple (top left)")],
  777.                          [_("Staple (bottom left)")],
  778.                          [_("Staple (top right)")],
  779.                          [_("Staple (bottom right)")],
  780.                          [_("Edge stitch (left)")],
  781.                          [_("Edge stitch (top)")],
  782.                          [_("Edge stitch (right)")],
  783.                          [_("Edge stitch (bottom)")],
  784.                          [_("Staple dual (left)")],
  785.                          [_("Staple dual (top)")],
  786.                          [_("Staple dual (right)")],
  787.                          [_("Staple dual (bottom)")],
  788.                          [_("Bind (left)")],
  789.                          [_("Bind (top)")],
  790.                          [_("Bind (right)")],
  791.                          [_("Bind (bottom)")]]),
  792.  
  793.                        (self.cmbJOMedia, []),
  794.  
  795.                        (self.cmbJOSides,
  796.                         [[_("One-sided")],
  797.                          [_("Two-sided (long edge)")],
  798.                          [_("Two-sided (short edge)")]]),
  799.  
  800.                        (self.cmbJOHoldUntil, []),
  801.  
  802.                        (self.cmbJOOutputOrder,
  803.                         [[_("Normal")],
  804.                          [_("Reverse")]]),
  805.  
  806.                        ]:
  807.             model = gtk.ListStore (gobject.TYPE_STRING)
  808.             for row in opts:
  809.                 model.append (row=row)
  810.  
  811.             cell = gtk.CellRendererText ()
  812.             widget.pack_start (cell, True)
  813.             widget.add_attribute (cell, 'text', 0)
  814.             widget.set_model (model)
  815.  
  816.         opts = [ options.OptionAlwaysShown ("copies", int, 1,
  817.                                             self.sbJOCopies,
  818.                                             self.btnJOResetCopies),
  819.  
  820.                  options.OptionAlwaysShownSpecial \
  821.                  ("orientation-requested", int, 3,
  822.                   self.cmbJOOrientationRequested,
  823.                   self.btnJOResetOrientationRequested,
  824.                   combobox_map = [3, 4, 5, 6],
  825.                   special_choice=_("Automatic rotation")),
  826.  
  827.                  options.OptionAlwaysShown ("fitplot", bool, False,
  828.                                             self.cbJOFitplot,
  829.                                             self.btnJOResetFitplot),
  830.  
  831.                  options.OptionAlwaysShown ("number-up", int, 1,
  832.                                             self.cmbJONumberUp,
  833.                                             self.btnJOResetNumberUp,
  834.                                             combobox_map=[1, 2, 4, 6, 9, 16]),
  835.  
  836.                  options.OptionAlwaysShown ("number-up-layout", str, "lrtb",
  837.                                             self.cmbJONumberUpLayout,
  838.                                             self.btnJOResetNumberUpLayout,
  839.                                             combobox_map = [ "lrtb",
  840.                                                              "lrbt",
  841.                                                              "rltb",
  842.                                                              "rlbt",
  843.                                                              "tblr",
  844.                                                              "tbrl",
  845.                                                              "btlr",
  846.                                                              "btrl" ]),
  847.  
  848.                  options.OptionAlwaysShown ("brightness", int, 100,
  849.                                             self.sbJOBrightness,
  850.                                             self.btnJOResetBrightness),
  851.  
  852.                  options.OptionAlwaysShown ("finishings", int, 3,
  853.                                             self.cmbJOFinishings,
  854.                                             self.btnJOResetFinishings,
  855.                                             combobox_map = [ 3, 4, 5, 6,
  856.                                                              7, 8, 9, 10,
  857.                                                              11, 12, 13, 14,
  858.                                                              20, 21, 22, 23,
  859.                                                              24, 25, 26, 27,
  860.                                                              28, 29, 30, 31,
  861.                                                              50, 51, 52, 53 ],
  862.                                             use_supported = True),
  863.  
  864.                  options.OptionAlwaysShown ("job-priority", int, 50,
  865.                                             self.sbJOJobPriority,
  866.                                             self.btnJOResetJobPriority),
  867.  
  868.                  options.OptionAlwaysShown ("media", str,
  869.                                             "A4", # This is the default for
  870.                                                   # when media-default is
  871.                                                   # not supplied by the IPP
  872.                                                   # server.  Fortunately it
  873.                                                   # is a mandatory attribute.
  874.                                             self.cmbJOMedia,
  875.                                             self.btnJOResetMedia,
  876.                                             use_supported = True),
  877.  
  878.                  options.OptionAlwaysShown ("sides", str, "one-sided",
  879.                                             self.cmbJOSides,
  880.                                             self.btnJOResetSides,
  881.                                             combobox_map =
  882.                                             [ "one-sided",
  883.                                               "two-sided-long-edge",
  884.                                               "two-sided-short-edge" ]),
  885.  
  886.                  options.OptionAlwaysShown ("job-hold-until", str,
  887.                                             "no-hold",
  888.                                             self.cmbJOHoldUntil,
  889.                                             self.btnJOResetHoldUntil,
  890.                                             use_supported = True),
  891.  
  892.                  options.OptionAlwaysShown ("outputorder", str,
  893.                                             "normal",
  894.                                             self.cmbJOOutputOrder,
  895.                                             self.btnJOResetOutputOrder,
  896.                                             combobox_map =
  897.                                             [ "normal",
  898.                                               "reverse" ]),
  899.  
  900.                  options.OptionAlwaysShown ("mirror", bool, False,
  901.                                             self.cbJOMirror,
  902.                                             self.btnJOResetMirror),
  903.  
  904.                  options.OptionAlwaysShown ("scaling", int, 100,
  905.                                             self.sbJOScaling,
  906.                                             self.btnJOResetScaling),
  907.  
  908.                  options.OptionAlwaysShown ("saturation", int, 100,
  909.                                             self.sbJOSaturation,
  910.                                             self.btnJOResetSaturation),
  911.  
  912.                  options.OptionAlwaysShown ("hue", int, 0,
  913.                                             self.sbJOHue,
  914.                                             self.btnJOResetHue),
  915.  
  916.                  options.OptionAlwaysShown ("gamma", int, 1000,
  917.                                             self.sbJOGamma,
  918.                                             self.btnJOResetGamma),
  919.  
  920.                  options.OptionAlwaysShown ("cpi", float, 10.0,
  921.                                             self.sbJOCpi, self.btnJOResetCpi),
  922.  
  923.                  options.OptionAlwaysShown ("lpi", float, 6.0,
  924.                                             self.sbJOLpi, self.btnJOResetLpi),
  925.  
  926.                  options.OptionAlwaysShown ("page-left", int, 0,
  927.                                             self.sbJOPageLeft,
  928.                                             self.btnJOResetPageLeft),
  929.  
  930.                  options.OptionAlwaysShown ("page-right", int, 0,
  931.                                             self.sbJOPageRight,
  932.                                             self.btnJOResetPageRight),
  933.  
  934.                  options.OptionAlwaysShown ("page-top", int, 0,
  935.                                             self.sbJOPageTop,
  936.                                             self.btnJOResetPageTop),
  937.  
  938.                  options.OptionAlwaysShown ("page-bottom", int, 0,
  939.                                             self.sbJOPageBottom,
  940.                                             self.btnJOResetPageBottom),
  941.  
  942.                  options.OptionAlwaysShown ("prettyprint", bool, False,
  943.                                             self.cbJOPrettyPrint,
  944.                                             self.btnJOResetPrettyPrint),
  945.  
  946.                  options.OptionAlwaysShown ("wrap", bool, False, self.cbJOWrap,
  947.                                             self.btnJOResetWrap),
  948.  
  949.                  options.OptionAlwaysShown ("columns", int, 1,
  950.                                             self.sbJOColumns,
  951.                                             self.btnJOResetColumns),
  952.                  ]
  953.         self.job_options_widgets = {}
  954.         self.job_options_buttons = {}
  955.         for option in opts:
  956.             self.job_options_widgets[option.widget] = option
  957.             self.job_options_buttons[option.button] = option
  958.  
  959.         self.monitor = monitor.Monitor (self, monitor_jobs=False)
  960.  
  961.         try:
  962.             self.populateList()
  963.         except cups.HTTPError, (s,):
  964.             self.cups = None
  965.             self.setConnected()
  966.             self.populateList()
  967.             show_HTTP_Error(s, self.PrintersWindow)
  968.  
  969.         if len (self.printers) > 3:
  970.             self.PrintersWindow.set_default_size (550, 400)
  971.  
  972.         self.PrintersWindow.show()
  973.  
  974.         if setup_printer:
  975.             self.device_uri = setup_printer
  976.             self.devid = devid
  977.             self.ppd = None
  978.             try:
  979.                 self.on_autodetected_printer_without_driver(None)
  980.             except RuntimeError:
  981.                 pass
  982.  
  983.         if configure_printer:
  984.             # Need to find the entry in the iconview model and activate it.
  985.             try:
  986.                 self.display_properties_dialog_for (configure_printer)
  987.                 if print_test_page:
  988.                     self.btnPrintTestPage.clicked ()
  989.                 if change_ppd:
  990.                     self.btnChangePPD.clicked ()
  991.             except RuntimeError:
  992.                 pass
  993.  
  994.     def display_properties_dialog_for (self, queue):
  995.         model = self.dests_iconview.get_model ()
  996.         iter = model.get_iter_first ()
  997.         while iter != None:
  998.             name = unicode (model.get_value (iter, 2))
  999.             if name == queue:
  1000.                 path = model.get_path (iter)
  1001.                 self.dests_iconview.scroll_to_path (path, True, 0.5, 0.5)
  1002.                 self.dests_iconview.set_cursor (path)
  1003.                 self.dests_iconview.item_activated (path)
  1004.                 break
  1005.             iter = model.iter_next (iter)
  1006.  
  1007.         if iter == None:
  1008.             raise RuntimeError
  1009.  
  1010.     def setup_toolbar_for_search_entry (self):
  1011.         separator = gtk.SeparatorToolItem ()
  1012.         separator.set_draw (False)
  1013.  
  1014.         self.toolbar.insert (separator, -1)
  1015.         self.toolbar.child_set_property (separator, "expand", True)
  1016.  
  1017.         self.search_entry = ToolbarSearchEntry ()
  1018.         self.search_entry.connect ('search', self.on_search_entry_search)
  1019.  
  1020.         tool_item = gtk.ToolItem ()
  1021.         tool_item.add (self.search_entry)
  1022.         self.toolbar.insert (tool_item, -1)
  1023.         self.toolbar.show_all ()
  1024.  
  1025.     def on_search_entry_search (self, UNUSED, text):
  1026.         self.ui_manager.get_action ("/save-as-group").set_sensitive (
  1027.             text and True or False)
  1028.         self.ui_manager.get_action ("/save-as-search-group").set_sensitive (
  1029.             text and True or False)
  1030.         self.current_filter_text = text
  1031.         self.populateList ()
  1032.  
  1033.     def on_groups_pane_item_activated (self, UNUSED, item):
  1034.         self.search_entry.clear ()
  1035.  
  1036.         if isinstance (item, SavedSearchGroupItem):
  1037.             crit = item.criteria[0]
  1038.             if crit.subject == SearchCriterion.SUBJECT_NAME:
  1039.                 self.ui_manager.get_action ("/filter-name").activate ()
  1040.             elif crit.subject == SearchCriterion.SUBJECT_DESC:
  1041.                 self.ui_manager.get_action ("/filter-description").activate ()
  1042.             elif crit.subject == SearchCriterion.SUBJECT_LOCATION:
  1043.                 self.ui_manager.get_action ("/filter-location").activate ()
  1044.             elif crit.subject == SearchCriterion.SUBJECT_MANUF:
  1045.                 self.ui_manager.get_action ("/filter-manufacturer").activate ()
  1046.             else:
  1047.                 nonfatalException ()
  1048.  
  1049.             self.search_entry.set_text (crit.value)
  1050.  
  1051.         self.current_groups_pane_item = item
  1052.         self.populateList ()
  1053.  
  1054.     def on_add_to_group_menu_item_activate (self, menuitem, group):
  1055.         group.add_queues (self.groups_pane.currently_selected_queues)
  1056.  
  1057.     def update_add_to_group_menu (self):
  1058.         for child in self.add_to_group_menu.get_children ():
  1059.             self.add_to_group_menu.remove (child)
  1060.         static_groups = self.groups_pane.get_static_groups ()
  1061.         for group in static_groups:
  1062.             item = gtk.MenuItem (group.name, False)
  1063.             item.connect ("activate",
  1064.                           self.on_add_to_group_menu_item_activate, group)
  1065.             self.add_to_group_menu.append (item)
  1066.         if len (static_groups) > 0:
  1067.             item = gtk.SeparatorMenuItem ()
  1068.             self.add_to_group_menu.append (item)
  1069.         action = self.groups_pane.ui_manager.get_action ("/new-group-from-selection")
  1070.         item = action.create_menu_item ()
  1071.         self.add_to_group_menu.append (item)
  1072.         self.add_to_group_menu.show_all ()
  1073.  
  1074.     def on_groups_pane_items_changed (self, UNUSED):
  1075.         if not self.groups_pane_visible:
  1076.             self.view_groups.set_active (True)
  1077.         self.update_add_to_group_menu ()
  1078.  
  1079.     def on_filter_criterion_changed (self, UNUSED, selected_action):
  1080.         self.current_filter_mode = selected_action.get_name ()
  1081.         self.populateList ()
  1082.  
  1083.     def dests_iconview_item_activated (self, iconview, path):
  1084.         model = iconview.get_model ()
  1085.         iter = model.get_iter (path)
  1086.         name = unicode (model.get_value (iter, 2))
  1087.         object = model.get_value (iter, 0)
  1088.  
  1089.         try:
  1090.             self.fillPrinterTab (name)
  1091.         except cups.IPPError, (e, m):
  1092.             show_IPP_Error (e, m, self.PrintersWindow)
  1093.             if e == cups.IPP_SERVICE_UNAVAILABLE:
  1094.                 self.cups = None
  1095.                 self.setConnected ()
  1096.                 self.populateList ()
  1097.             return
  1098.         except RuntimeError:
  1099.             # Perhaps cupsGetPPD2 failed for a browsed printer.
  1100.             return
  1101.  
  1102.         self.PrinterPropertiesDialog.set_transient_for (self.PrintersWindow)
  1103.         for button in [self.btnPrinterPropertiesCancel,
  1104.                        self.btnPrinterPropertiesOK,
  1105.                        self.btnPrinterPropertiesApply]:
  1106.             if object.discovered:
  1107.                 button.hide ()
  1108.             else:
  1109.                 button.show ()
  1110.         if object.discovered:
  1111.             self.btnPrinterPropertiesClose.show ()
  1112.         else:
  1113.             self.btnPrinterPropertiesClose.hide ()
  1114.         self.setDataButtonState ()
  1115.         self.btnPrintTestPage.set_tooltip_text(_("CUPS test page"))
  1116.         self.btnSelfTest.set_tooltip_text(_("Typically shows whether all jets "
  1117.                                             "on a print head are functioning "
  1118.                                             "and that the print feed mechanisms"
  1119.                                             " are working properly."))
  1120.         treeview = self.tvPrinterProperties
  1121.         treeview.set_cursor ((0,))
  1122.         host = CUPS_server_hostname ()
  1123.         self.PrinterPropertiesDialog.set_title (_("Printer Properties - "
  1124.                                                   "'%s' on %s") % (name, host))
  1125.         self.PrinterPropertiesDialog.set_focus_on_map (self.focus_on_map)
  1126.         self.PrinterPropertiesDialog.show ()
  1127.  
  1128.     def printer_properties_response (self, dialog, response):
  1129.         if response == gtk.RESPONSE_REJECT:
  1130.             # The Conflict button was pressed.
  1131.             message = _("There are conflicting options.\n"
  1132.                         "Changes can only be applied after\n"
  1133.                         "these conflicts are resolved.")
  1134.             message += "\n\n"
  1135.             for option in self.conflicts:
  1136.                 message += option.option.text + "\n"
  1137.  
  1138.             dialog = gtk.MessageDialog(self.PrinterPropertiesDialog,
  1139.                                        gtk.DIALOG_DESTROY_WITH_PARENT |
  1140.                                        gtk.DIALOG_MODAL,
  1141.                                        gtk.MESSAGE_WARNING,
  1142.                                        gtk.BUTTONS_CLOSE,
  1143.                                        message)
  1144.             dialog.run()
  1145.             dialog.destroy()
  1146.             return
  1147.  
  1148.         if (response == gtk.RESPONSE_OK or
  1149.             response == gtk.RESPONSE_APPLY):
  1150.             failed = self.save_printer (self.printer)
  1151.  
  1152.         if response == gtk.RESPONSE_APPLY and not failed:
  1153.             try:
  1154.                 self.fillPrinterTab (self.printer.name)
  1155.             except:
  1156.                 pass
  1157.  
  1158.             self.setDataButtonState ()
  1159.  
  1160.         if ((response == gtk.RESPONSE_OK and not failed) or
  1161.             response == gtk.RESPONSE_CANCEL):
  1162.             self.printer = None
  1163.             dialog.hide ()
  1164.  
  1165.             if self.newPrinterGUI.NewPrinterWindow.get_property ("visible"):
  1166.                 self.newPrinterGUI.on_NPCancel (None)
  1167.  
  1168.     def dests_iconview_selection_changed (self, iconview):
  1169.         self.updating_widgets = True
  1170.         paths = iconview.get_selected_items ()
  1171.         any_disabled = False
  1172.         any_enabled = False
  1173.         any_discovered = False
  1174.         any_shared = False
  1175.         any_unshared = False
  1176.         self.groups_pane.currently_selected_queues = []
  1177.         model = iconview.get_model ()
  1178.         for path in paths:
  1179.             iter = model.get_iter (path)
  1180.             object = model.get_value (iter, 0)
  1181.             name = unicode (model.get_value (iter, 2))
  1182.             self.groups_pane.currently_selected_queues.append (name)
  1183.             if object.discovered:
  1184.                 any_discovered = True
  1185.             if object.enabled:
  1186.                 any_enabled = True
  1187.             else:
  1188.                 any_disabled = True
  1189.             if object.is_shared:
  1190.                 any_shared = True
  1191.             else:
  1192.                 any_unshared = True
  1193.  
  1194.         n = len (paths)
  1195.         self.groups_pane.ui_manager.get_action (
  1196.             "/new-group-from-selection").set_sensitive (n > 0)
  1197.  
  1198.         self.ui_manager.get_action ("/edit-printer").set_sensitive (n == 1)
  1199.  
  1200.         self.ui_manager.get_action ("/duplicate-printer").set_sensitive (n == 1)
  1201.  
  1202.         self.ui_manager.get_action ("/rename-printer").set_sensitive (
  1203.             n == 1 and not any_discovered)
  1204.  
  1205.         userdef = userdefault.UserDefaultPrinter ().get ()
  1206.         if (n != 1 or
  1207.             (userdef == None and self.default_printer == name)):
  1208.             set_default_sensitivity = False
  1209.         else:
  1210.             set_default_sensitivity = True
  1211.  
  1212.         self.ui_manager.get_action ("/set-default-printer").set_sensitive (
  1213.             set_default_sensitivity)
  1214.  
  1215.         action = self.ui_manager.get_action ("/enable-printer")
  1216.         action.set_sensitive (n > 0 and not any_discovered)
  1217.         for widget in action.get_proxies ():
  1218.             if isinstance (widget, gtk.CheckMenuItem):
  1219.                 widget.set_inconsistent (n > 1 and any_enabled and any_disabled)
  1220.         action.set_active (any_discovered or not any_disabled)
  1221.  
  1222.         action = self.ui_manager.get_action ("/share-printer")
  1223.         action.set_sensitive (n > 0 and not any_discovered)
  1224.         for widget in action.get_proxies ():
  1225.             if isinstance (widget, gtk.CheckMenuItem):
  1226.                 widget.set_inconsistent (n > 1 and any_shared and any_unshared)
  1227.         action.set_active (any_discovered or not any_unshared)
  1228.  
  1229.         self.ui_manager.get_action ("/delete-printer").set_sensitive (
  1230.             n > 0 and not any_discovered)
  1231.  
  1232.         self.ui_manager.get_action ("/create-class").set_sensitive (n > 1)
  1233.  
  1234.         self.ui_manager.get_action ("/add-to-group").set_sensitive (n > 0)
  1235.  
  1236.         self.updating_widgets = False
  1237.  
  1238.     def dests_iconview_popup_menu (self, iconview):
  1239.         self.printer_context_menu.popup (None, None, None, 0, 0L)
  1240.  
  1241.     def dests_iconview_button_press_event (self, iconview, event):
  1242.         if event.button > 1:
  1243.             click_path = iconview.get_path_at_pos (int (event.x),
  1244.                                                    int (event.y))
  1245.             paths = iconview.get_selected_items ()
  1246.             if click_path == None:
  1247.                 iconview.unselect_all ()
  1248.             elif click_path not in paths:
  1249.                 iconview.unselect_all ()
  1250.                 iconview.select_path (click_path)
  1251.                 cells = iconview.get_cells ()
  1252.                 for cell in cells:
  1253.                     if type (cell) == gtk.CellRendererText:
  1254.                         break
  1255.                 iconview.set_cursor (click_path, cell)
  1256.             self.printer_context_menu.popup (None, None, None,
  1257.                                              event.button, event.time)
  1258.         return False
  1259.  
  1260.     def dests_iconview_key_press_event (self, iconview, event):
  1261.         modifiers = gtk.accelerator_get_default_mod_mask ()
  1262.  
  1263.         if ((event.keyval == gtk.keysyms.BackSpace or
  1264.              event.keyval == gtk.keysyms.Delete or
  1265.              event.keyval == gtk.keysyms.KP_Delete) and
  1266.             ((event.state & modifiers) == 0)):
  1267.  
  1268.             self.ui_manager.get_action ("/delete-printer").activate ()
  1269.             return True
  1270.  
  1271.         if ((event.keyval == gtk.keysyms.F2) and
  1272.             ((event.state & modifiers) == 0)):
  1273.  
  1274.             self.ui_manager.get_action ("/rename-printer").activate ()
  1275.             return True
  1276.  
  1277.         return False
  1278.  
  1279.     def dests_iconview_drag_data_get (self, iconview, context,
  1280.                                       selection_data, info, timestamp):
  1281.         if info == 0: # FIXME: should use an "enum" here
  1282.             model = iconview.get_model ()
  1283.             paths = iconview.get_selected_items ()
  1284.             selected_printer_names = ""
  1285.             for path in paths:
  1286.                 selected_printer_names += \
  1287.                     model.get_value (model.get_iter (path), 2) + "\n"
  1288.  
  1289.             if len (selected_printer_names) > 0:
  1290.                 selection_data.set ("queue", 8, selected_printer_names)
  1291.         else:
  1292.             nonfatalException ()
  1293.  
  1294.     def on_server_settings_activate (self, menuitem):
  1295.         try:
  1296.             self.fillServerTab ()
  1297.             self.advancedServerSettings = AdvancedServerSettings(self,
  1298.                                              self.on_adv_server_settings_apply)
  1299.         except (cups.IPPError, cups.HTTPError):
  1300.             # Not authorized.
  1301.             return
  1302.  
  1303.         self.ServerSettingsDialog.set_transient_for (self.PrintersWindow)
  1304.         self.ServerSettingsDialog.show ()
  1305.  
  1306.     def server_settings_response (self, dialog, response):
  1307.         self.advancedServerSettings.on_response(dialog, response)
  1308.         if response == gtk.RESPONSE_OK:
  1309.             if not self.save_serversettings ():
  1310.                 dialog.hide ()
  1311.         else:
  1312.             dialog.hide ()
  1313.  
  1314.     # Add button on 'Advanced Server Settings' clicked
  1315.     def on_adv_server_add_clicked (self, button):
  1316.         self.advancedServerSettings.on_add_clicked(button)
  1317.  
  1318.     # Remove button on 'Advanced Server Settings' clicked
  1319.     def on_adv_server_remove_clicked (self, button):
  1320.         self.advancedServerSettings.on_remove_clicked(button)
  1321.  
  1322.     def on_adv_server_settings_apply (self):
  1323.         self.cups._begin_operation (_("fetching server settings"))
  1324.         try:
  1325.             self.server_settings = self.cups.adminGetServerSettings()
  1326.         except cups.IPPError, (e, m):
  1327.             show_IPP_Error(e, m, self.PrintersWindow)
  1328.             self.cups._end_operation ()
  1329.             raise
  1330.         self.cups._end_operation ()
  1331.  
  1332.     def busy (self, win = None):
  1333.         try:
  1334.             if not win:
  1335.                 win = self.PrintersWindow
  1336.             gdkwin = win.window
  1337.             if gdkwin:
  1338.                 gdkwin.set_cursor (busy_cursor)
  1339.                 while gtk.events_pending ():
  1340.                     gtk.main_iteration ()
  1341.         except:
  1342.             nonfatalException ()
  1343.  
  1344.     def ready (self, win = None):
  1345.         try:
  1346.             if not win:
  1347.                 win = self.PrintersWindow
  1348.             gdkwin = win.window
  1349.             if gdkwin:
  1350.                 gdkwin.set_cursor (None)
  1351.                 while gtk.events_pending ():
  1352.                     gtk.main_iteration ()
  1353.         except:
  1354.             nonfatalException ()
  1355.  
  1356.     def setConnected(self):
  1357.         connected = bool(self.cups)
  1358.  
  1359.         host = CUPS_server_hostname ()
  1360.         self.PrintersWindow.set_title(_("Printing - %s") % host)
  1361.         self.PrintersWindow.set_focus_on_map (self.focus_on_map)
  1362.  
  1363.         if connected:
  1364.             status_msg = _("Connected to %s") % host
  1365.         else:
  1366.             status_msg = _("Not connected")
  1367.         self.statusbarMain.push(self.status_context_id, status_msg)
  1368.  
  1369.         for widget in (self.btnNew,
  1370.                        self.menuItemNew,
  1371.                        self.chkServerBrowse, self.chkServerShare,
  1372.                        self.chkServerRemoteAdmin,
  1373.                        self.chkServerAllowCancelAll,
  1374.                        self.chkServerLogDebug):
  1375.             widget.set_sensitive(connected)
  1376.  
  1377.         for action_name in ["/server-settings",
  1378.                             "/new-printer",
  1379.                             "/new-class"]:
  1380.             action = self.ui_manager.get_action (action_name)
  1381.             action.set_sensitive (connected)
  1382.  
  1383.         sharing = self.chkServerShare.get_active ()
  1384.         self.chkServerShareAny.set_sensitive (sharing)
  1385.  
  1386.         try:
  1387.             del self.server_settings
  1388.         except:
  1389.             pass
  1390.  
  1391.     def getServers(self):
  1392.         self.servers.discard(None)
  1393.         known_servers = list(self.servers)
  1394.         known_servers.sort()
  1395.         return known_servers
  1396.  
  1397.     def populateList(self, prompt_allowed=True):
  1398.         # Save selection of printers.
  1399.         selected_printers = set()
  1400.         paths = self.dests_iconview.get_selected_items ()
  1401.         model = self.dests_iconview.get_model ()
  1402.         for path in paths:
  1403.             iter = model.get_iter (path)
  1404.             name = unicode (model.get_value (iter, 2))
  1405.             selected_printers.add (name)
  1406.  
  1407.         if self.cups:
  1408.             self.cups._set_prompt_allowed (prompt_allowed)
  1409.             self.cups._begin_operation (_("obtaining queue details"))
  1410.             try:
  1411.                 # get Printers
  1412.                 self.printers = cupshelpers.getPrinters(self.cups)
  1413.  
  1414.                 # Get default printer.
  1415.                 self.default_printer = self.cups.getDefault ()
  1416.             except cups.IPPError, (e, m):
  1417.                 show_IPP_Error(e, m, self.PrintersWindow)
  1418.                 self.printers = {}
  1419.                 self.default_printer = None
  1420.  
  1421.             self.cups._end_operation ()
  1422.             self.cups._set_prompt_allowed (True)
  1423.         else:
  1424.             self.printers = {}
  1425.             self.default_printer = None
  1426.  
  1427.         for name, printer in self.printers.iteritems():
  1428.             self.servers.add(printer.getServer())
  1429.  
  1430.         userdef = userdefault.UserDefaultPrinter ().get ()
  1431.  
  1432.         local_printers = []
  1433.         local_classes = []
  1434.         remote_printers = []
  1435.         remote_classes = []
  1436.  
  1437.         # Choose a view according to the groups pane item
  1438.         if (isinstance (self.current_groups_pane_item, AllPrintersItem) or
  1439.             isinstance (self.current_groups_pane_item, SavedSearchGroupItem)):
  1440.             delete_action = self.ui_manager.get_action ("/delete-printer")
  1441.             delete_action.set_properties (label = None)
  1442.             printers_set = self.printers
  1443.         elif isinstance (self.current_groups_pane_item, FavouritesItem):
  1444.             printers_set = {} # FIXME
  1445.         elif isinstance (self.current_groups_pane_item, StaticGroupItem):
  1446.             delete_action = self.ui_manager.get_action ("/delete-printer")
  1447.             delete_action.set_properties (label = _("Remove from Group"))
  1448.             printers_set = {}
  1449.             deleted_printers = []
  1450.             for printer_name in self.current_groups_pane_item.printer_queues:
  1451.                 try:
  1452.                     printer = self.printers[printer_name]
  1453.                     printers_set[printer_name] = printer
  1454.                 except KeyError:
  1455.                     deleted_printers.append (printer_name)
  1456.             self.current_groups_pane_item.remove_queues (deleted_printers)
  1457.         else:
  1458.             printers_set = self.printers
  1459.             nonfatalException ()
  1460.  
  1461.         # Filter printers
  1462.         if len (self.current_filter_text) > 0:
  1463.             printers_subset = {}
  1464.             pattern = re.compile (self.current_filter_text, re.I) # ignore case
  1465.  
  1466.             if self.current_filter_mode == "filter-name":
  1467.                 for name in printers_set.keys ():
  1468.                     if pattern.search (name) != None:
  1469.                         printers_subset[name] = printers_set[name]
  1470.             elif self.current_filter_mode == "filter-description":
  1471.                 for name, printer in printers_set.iteritems ():
  1472.                     if pattern.search (printer.info) != None:
  1473.                         printers_subset[name] = printers_set[name]
  1474.             elif self.current_filter_mode == "filter-location":
  1475.                 for name, printer in printers_set.iteritems ():
  1476.                     if pattern.search (printer.location) != None:
  1477.                         printers_subset[name] = printers_set[name]
  1478.             elif self.current_filter_mode == "filter-manufacturer":
  1479.                 for name, printer in printers_set.iteritems ():
  1480.                     if pattern.search (printer.make_and_model) != None:
  1481.                         printers_subset[name] = printers_set[name]
  1482.             else:
  1483.                 nonfatalException ()
  1484.  
  1485.             printers_set = printers_subset
  1486.  
  1487.         if not self.view_discovered_printers.get_active ():
  1488.             printers_subset = {}
  1489.             for name, printer in printers_set.iteritems ():
  1490.                 if not printer.discovered:
  1491.                     printers_subset[name] = printer
  1492.  
  1493.             printers_set = printers_subset
  1494.  
  1495.         for name, printer in printers_set.iteritems():
  1496.             if printer.remote:
  1497.                 if printer.is_class: remote_classes.append(name)
  1498.                 else: remote_printers.append(name)
  1499.             else:
  1500.                 if printer.is_class: local_classes.append(name)
  1501.                 else: local_printers.append(name)
  1502.  
  1503.         local_printers.sort()
  1504.         local_classes.sort()
  1505.         remote_printers.sort()
  1506.         remote_classes.sort()
  1507.  
  1508.         # remove old printers/classes
  1509.         self.mainlist.clear ()
  1510.  
  1511.         # add new
  1512.         PRINTER_TYPE = { 'discovered-printer':
  1513.                              (_("Network printer (discovered)"),
  1514.                               'i-network-printer'),
  1515.                          'discovered-class':
  1516.                              (_("Network class (discovered)"),
  1517.                               'i-network-printer'),
  1518.                          'local-printer':
  1519.                              (_("Printer"),
  1520.                               'printer'),
  1521.                          'local-fax':
  1522.                              (_("Fax"),
  1523.                               'printer'),
  1524.                          'local-class':
  1525.                              (_("Class"),
  1526.                               'printer'),
  1527.                          'ipp-printer':
  1528.                              (_("Network printer"),
  1529.                               'i-network-printer'),
  1530.                          'smb-printer':
  1531.                              (_("Network print share"),
  1532.                               'printer'),
  1533.                          'network-printer':
  1534.                              (_("Network printer"),
  1535.                               'i-network-printer'),
  1536.                          }
  1537.         theme = gtk.icon_theme_get_default ()
  1538.         for printers in (local_printers,
  1539.                          local_classes,
  1540.                          remote_printers,
  1541.                          remote_classes):
  1542.             if not printers: continue
  1543.             for name in printers:
  1544.                 type = 'local-printer'
  1545.                 object = printers_set[name]
  1546.                 if object.discovered:
  1547.                     if object.is_class:
  1548.                         type = 'discovered-class'
  1549.                     else:
  1550.                         type = 'discovered-printer'
  1551.                 elif object.is_class:
  1552.                     type = 'local-class'
  1553.                 else:
  1554.                     (scheme, rest) = urllib.splittype (object.device_uri)
  1555.                     if scheme == 'ipp':
  1556.                         type = 'ipp-printer'
  1557.                     elif scheme == 'smb':
  1558.                         type = 'smb-printer'
  1559.                     elif scheme == 'hpfax':
  1560.                         type = 'local-fax'
  1561.                     elif scheme in ['socket', 'lpd']:
  1562.                         type = 'network-printer'
  1563.  
  1564.                 (tip, icon) = PRINTER_TYPE[type]
  1565.                 (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
  1566.                 try:
  1567.                     pixbuf = theme.load_icon (icon, w, 0)
  1568.                 except gobject.GError:
  1569.                     # Not in theme.
  1570.                     pixbuf = None
  1571.                     for p in [iconpath, 'icons/']:
  1572.                         try:
  1573.                             pixbuf = gtk.gdk.pixbuf_new_from_file ("%s%s.png" %
  1574.                                                                    (p, icon))
  1575.                             break
  1576.                         except gobject.GError:
  1577.                             pass
  1578.  
  1579.                     if pixbuf == None:
  1580.                         try:
  1581.                             pixbuf = theme.load_icon ('printer', w, 0)
  1582.                         except:
  1583.                             # Just create an empty pixbuf.
  1584.                             pixbuf = gtk.gdk.Pixbuf (gtk.gdk.COLORSPACE_RGB,
  1585.                                                      True, 8, w, h)
  1586.                             pixbuf.fill (0)
  1587.  
  1588.                 def_emblem = None
  1589.                 emblem = None
  1590.                 if name == self.default_printer:
  1591.                     def_emblem = 'emblem-default'
  1592.                 elif name == userdef:
  1593.                     def_emblem = 'emblem-favorite'
  1594.  
  1595.                 if not emblem:
  1596.                     attrs = object.other_attributes
  1597.                     reasons = attrs.get ('printer-state-reasons', [])
  1598.                     worst_reason = None
  1599.                     for reason in reasons:
  1600.                         if reason == "none":
  1601.                             break
  1602.  
  1603.                         if reason == "paused":
  1604.                             emblem = gtk.STOCK_MEDIA_PAUSE
  1605.                             continue
  1606.  
  1607.                         r = statereason.StateReason (object.connection,
  1608.                                                      object.name, reason)
  1609.                         if worst_reason == None:
  1610.                             worst_reason = r
  1611.                         elif r > worst_reason:
  1612.                             worst_reason = r
  1613.  
  1614.                     if worst_reason:
  1615.                         level = worst_reason.get_level ()
  1616.                         emblem = worst_reason.LEVEL_ICON[level]
  1617.  
  1618.                 if not emblem and not object.enabled:
  1619.                     emblem = gtk.STOCK_MEDIA_PAUSE
  1620.  
  1621.                 if object.rejecting:
  1622.                     # Show the icon as insensitive
  1623.                     copy = pixbuf.copy ()
  1624.                     copy.fill (0)
  1625.                     pixbuf.composite (copy, 0, 0,
  1626.                                       copy.get_width(), copy.get_height(),
  1627.                                       0, 0, 1.0, 1.0,
  1628.                                       gtk.gdk.INTERP_BILINEAR, 127)
  1629.                     pixbuf = copy
  1630.  
  1631.                 if def_emblem:
  1632.                     (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
  1633.                     try:
  1634.                         default_emblem = theme.load_icon (def_emblem, w/2, 0)
  1635.                         copy = pixbuf.copy ()
  1636.                         default_emblem.composite (copy, 0, 0,
  1637.                                                   copy.get_width (),
  1638.                                                   copy.get_height (),
  1639.                                                   0, 0,
  1640.                                                   1.0, 1.0,
  1641.                                                   gtk.gdk.INTERP_NEAREST, 255)
  1642.                         pixbuf = copy
  1643.                     except gobject.GError:
  1644.                         debugprint ("No %s icon available" % def_emblem)
  1645.  
  1646.                 if emblem:
  1647.                     (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
  1648.                     try:
  1649.                         other_emblem = theme.load_icon (emblem, w/2, 0)
  1650.                         copy = pixbuf.copy ()
  1651.                         other_emblem.composite (copy, 0, 0,
  1652.                                                 copy.get_width (),
  1653.                                                 copy.get_height (),
  1654.                                                 copy.get_width () / 2,
  1655.                                                 copy.get_height () / 2,
  1656.                                                 1.0, 1.0,
  1657.                                                 gtk.gdk.INTERP_NEAREST, 255)
  1658.                         pixbuf = copy
  1659.                     except gobject.GError:
  1660.                         debugprint ("No %s icon available" % emblem)
  1661.  
  1662.                 self.mainlist.append (row=[object, pixbuf, name, tip])
  1663.  
  1664.         # Restore selection of printers.
  1665.         model = self.dests_iconview.get_model ()
  1666.         def maybe_select (model, path, iter):
  1667.             name = unicode (model.get_value (iter, 2))
  1668.             if name in selected_printers:
  1669.                 self.dests_iconview.select_path (path)
  1670.         model.foreach (maybe_select)
  1671.  
  1672.         if (self.printer != None and
  1673.             self.printer.name not in self.printers.keys ()):
  1674.             # The printer we're editing has been deleted.
  1675.             self.PrinterPropertiesDialog.response (gtk.RESPONSE_CANCEL)
  1676.  
  1677.     # Connect to Server
  1678.  
  1679.     def on_connect_servername_changed(self, widget):
  1680.         self.btnConnect.set_sensitive (len (widget.get_active_text ()) > 0)
  1681.  
  1682.     def on_connect_activate(self, widget):
  1683.         # Use browsed queues to build up a list of known IPP servers
  1684.         servers = self.getServers()
  1685.         current_server = (self.printer and self.printer.getServer()) \
  1686.                          or cups.getServer()
  1687.  
  1688.         store = gtk.ListStore (gobject.TYPE_STRING)
  1689.         self.cmbServername.set_model(store)
  1690.         self.cmbServername.set_text_column (0)
  1691.         for server in servers:
  1692.             self.cmbServername.append_text(server)
  1693.         self.cmbServername.show()
  1694.  
  1695.         self.cmbServername.child.set_text (current_server)
  1696.         self.chkEncrypted.set_active (cups.getEncryption() ==
  1697.                                       cups.HTTP_ENCRYPT_ALWAYS)
  1698.  
  1699.         self.cmbServername.child.set_activates_default (True)
  1700.         self.cmbServername.grab_focus ()
  1701.         self.ConnectDialog.set_transient_for (self.PrintersWindow)
  1702.         response = self.ConnectDialog.run()
  1703.  
  1704.         self.ConnectDialog.hide()
  1705.  
  1706.         if response != gtk.RESPONSE_OK:
  1707.             return
  1708.  
  1709.         if self.chkEncrypted.get_active():
  1710.             cups.setEncryption(cups.HTTP_ENCRYPT_ALWAYS)
  1711.         else:
  1712.             cups.setEncryption(cups.HTTP_ENCRYPT_IF_REQUESTED)
  1713.         self.connect_encrypt = cups.getEncryption ()
  1714.  
  1715.         servername = self.cmbServername.child.get_text()
  1716.  
  1717.         self.lblConnecting.set_markup(_("<i>Opening connection to %s</i>") %
  1718.                                       servername)
  1719.         self.newPrinterGUI.dropPPDs()
  1720.         self.ConnectingDialog.set_transient_for(self.PrintersWindow)
  1721.         self.ConnectingDialog.show()
  1722.         gobject.timeout_add (40, self.update_connecting_pbar)
  1723.         self.connect_server = servername
  1724.         # We need to set the connecting user in this thread as well.
  1725.         cups.setServer(self.connect_server)
  1726.         cups.setUser('')
  1727.         self.connect_user = cups.getUser()
  1728.         # Now start a new thread for connection.
  1729.         self.connect_thread = thread.start_new_thread(self.connect,
  1730.                                                       (self.PrintersWindow,))
  1731.  
  1732.     def update_connecting_pbar (self):
  1733.         ret = True
  1734.         gtk.gdk.threads_enter ()
  1735.         if not self.ConnectingDialog.get_property ("visible"):
  1736.             ret = False # stop animation
  1737.         else:
  1738.             self.pbarConnecting.pulse ()
  1739.  
  1740.         gtk.gdk.threads_leave ()
  1741.         return ret
  1742.  
  1743.     def on_connectingdialog_delete (self, widget, event):
  1744.         self.on_cancel_connect_clicked (widget)
  1745.         return True
  1746.  
  1747.     def on_cancel_connect_clicked(self, widget):
  1748.         """
  1749.         Stop connection to new server
  1750.         (Doesn't really stop but sets flag for the connecting thread to
  1751.         ignore the connection)
  1752.         """
  1753.         self.connect_thread = None
  1754.         self.ConnectingDialog.hide()
  1755.  
  1756.     def connect(self, parent=None):
  1757.         """
  1758.         Open a connection to a new server. Is executed in a separate thread!
  1759.         """
  1760.         cups.setUser(self.connect_user)
  1761.         if self.connect_server[0] == '/':
  1762.             # UNIX domain socket.  This may potentially fail if the server
  1763.             # settings have been changed and cupsd has written out a
  1764.             # configuration that does not include a Listen line for the
  1765.             # UNIX domain socket.  To handle this special case, try to
  1766.             # connect once and fall back to "localhost" on failure.
  1767.             try:
  1768.                 connection = cups.Connection (host=self.connect_server,
  1769.                                               encryption=self.connect_encrypt)
  1770.  
  1771.                 # Worked fine.  Disconnect, and we'll connect for real
  1772.                 # shortly.
  1773.                 del connection
  1774.             except RuntimeError:
  1775.                 # When we connect, avoid the domain socket.
  1776.                 cups.setServer ("localhost")
  1777.             except:
  1778.                 nonfatalException ()
  1779.  
  1780.         try:
  1781.             connection = authconn.Connection(parent,
  1782.                                              host=self.connect_server,
  1783.                                              encryption=self.connect_encrypt)
  1784.             self.newPrinterGUI.dropPPDs ()
  1785.         except RuntimeError, s:
  1786.             if self.connect_thread != thread.get_ident(): return
  1787.             gtk.gdk.threads_enter()
  1788.             self.ConnectingDialog.hide()
  1789.             show_IPP_Error(None, s, parent)
  1790.             gtk.gdk.threads_leave()
  1791.             return
  1792.         except cups.IPPError, (e, s):
  1793.             if self.connect_thread != thread.get_ident(): return
  1794.             gtk.gdk.threads_enter()
  1795.             self.ConnectingDialog.hide()
  1796.             show_IPP_Error(e, s, parent)
  1797.             gtk.gdk.threads_leave()
  1798.             return
  1799.         except:
  1800.             nonfatalException ()
  1801.  
  1802.         if self.connect_thread != thread.get_ident(): return
  1803.         gtk.gdk.threads_enter()
  1804.  
  1805.         try:
  1806.             self.ConnectingDialog.hide()
  1807.             self.cups = connection
  1808.             self.setConnected()
  1809.             self.populateList()
  1810.     except cups.HTTPError, (s,):
  1811.             self.cups = None
  1812.             self.setConnected()
  1813.             self.populateList()
  1814.             show_HTTP_Error(s, parent)
  1815.         except:
  1816.             nonfatalException ()
  1817.  
  1818.         gtk.gdk.threads_leave()
  1819.  
  1820.     def reconnect (self):
  1821.         """Reconnect to CUPS after the server has reloaded."""
  1822.         # libcups would handle the reconnection if we just told it to
  1823.         # do something, for example fetching a list of classes.
  1824.         # However, our local authentication certificate would be
  1825.         # invalidated by a server restart, so it is better for us to
  1826.         # handle the reconnection ourselves.
  1827.  
  1828.         attempt = 1
  1829.         while attempt <= 5:
  1830.             try:
  1831.                 time.sleep(1)
  1832.                 self.cups._connect ()
  1833.                 break
  1834.             except RuntimeError:
  1835.                 # Connection failed.
  1836.                 attempt += 1
  1837.  
  1838.     def on_btnCancelConnect_clicked(self, widget):
  1839.         """Close Connect dialog"""
  1840.         self.ConnectWindow.hide()
  1841.  
  1842.     # refresh
  1843.  
  1844.     def on_btnRefresh_clicked(self, button):
  1845.         if self.cups == None:
  1846.             try:
  1847.                 self.cups = authconn.Connection(self.PrintersWindow)
  1848.             except RuntimeError:
  1849.                 pass
  1850.  
  1851.             self.setConnected()
  1852.  
  1853.         self.populateList()
  1854.  
  1855.     # Data handling
  1856.  
  1857.     def on_printer_changed(self, widget):
  1858.         if isinstance(widget, gtk.CheckButton):
  1859.             value = widget.get_active()
  1860.         elif isinstance(widget, gtk.Entry):
  1861.             value = widget.get_text()
  1862.         elif isinstance(widget, gtk.RadioButton):
  1863.             value = widget.get_active()
  1864.         elif isinstance(widget, gtk.ComboBox):
  1865.             model = widget.get_model ()
  1866.             iter = widget.get_active_iter()
  1867.             value = model.get_value (iter, 1)
  1868.         else:
  1869.             raise ValueError, "Widget type not supported (yet)"
  1870.  
  1871.         p = self.printer
  1872.         old_values = {
  1873.             self.entPDescription : p.info,
  1874.             self.entPLocation : p.location,
  1875.             self.entPDevice : p.device_uri,
  1876.             self.chkPEnabled : p.enabled,
  1877.             self.chkPAccepting : not p.rejecting,
  1878.             self.chkPShared : p.is_shared,
  1879.             self.cmbPStartBanner : p.job_sheet_start,
  1880.             self.cmbPEndBanner : p.job_sheet_end,
  1881.             self.cmbPErrorPolicy : p.error_policy,
  1882.             self.cmbPOperationPolicy : p.op_policy,
  1883.             self.rbtnPAllow: p.default_allow,
  1884.             }
  1885.  
  1886.         old_value = old_values[widget]
  1887.  
  1888.         if old_value == value:
  1889.             self.changed.discard(widget)
  1890.         else:
  1891.             self.changed.add(widget)
  1892.         self.setDataButtonState()
  1893.  
  1894.     def option_changed(self, option):
  1895.         if option.is_changed():
  1896.             self.changed.add(option)
  1897.         else:
  1898.             self.changed.discard(option)
  1899.  
  1900.         if option.conflicts:
  1901.             self.conflicts.add(option)
  1902.         else:
  1903.             self.conflicts.discard(option)
  1904.         self.setDataButtonState()
  1905.  
  1906.         if (self.option_manualfeed and self.option_inputslot and
  1907.             option == self.option_manualfeed):
  1908.             if option.get_current_value() == "True":
  1909.                 self.option_inputslot.disable ()
  1910.             else:
  1911.                 self.option_inputslot.enable ()
  1912.  
  1913.     # Access control
  1914.     def getPUsers(self):
  1915.         """return list of usernames from the GUI"""
  1916.         model = self.tvPUsers.get_model()
  1917.         result = []
  1918.         model.foreach(lambda model, path, iter:
  1919.                       result.append(model.get(iter, 0)[0]))
  1920.         result.sort()
  1921.         return result
  1922.  
  1923.     def setPUsers(self, users):
  1924.         """write list of usernames inot the GUI"""
  1925.         model = self.tvPUsers.get_model()
  1926.         model.clear()
  1927.         for user in users:
  1928.             model.append((user,))
  1929.  
  1930.         self.on_entPUser_changed(self.entPUser)
  1931.         self.on_tvPUsers_cursor_changed(self.tvPUsers)
  1932.  
  1933.     def checkPUsersChanged(self):
  1934.         """check if users in GUI and printer are different
  1935.         and set self.changed"""
  1936.         if self.getPUsers() != self.printer.except_users:
  1937.             self.changed.add(self.tvPUsers)
  1938.         else:
  1939.             self.changed.discard(self.tvPUsers)
  1940.  
  1941.         self.on_tvPUsers_cursor_changed(self.tvPUsers)
  1942.         self.setDataButtonState()
  1943.  
  1944.     def on_btnPAddUser_clicked(self, button):
  1945.         user = self.entPUser.get_text()
  1946.         if user:
  1947.             self.tvPUsers.get_model().insert(0, (user,))
  1948.             self.entPUser.set_text("")
  1949.         self.checkPUsersChanged()
  1950.  
  1951.     def on_btnPDelUser_clicked(self, button):
  1952.         model, rows = self.tvPUsers.get_selection().get_selected_rows()
  1953.         rows = [gtk.TreeRowReference(model, row) for row in rows]
  1954.         for row in rows:
  1955.             path = row.get_path()
  1956.             iter = model.get_iter(path)
  1957.             model.remove(iter)
  1958.         self.checkPUsersChanged()
  1959.  
  1960.     def on_entPUser_changed(self, widget):
  1961.         self.btnPAddUser.set_sensitive(bool(widget.get_text()))
  1962.  
  1963.     def on_tvPUsers_cursor_changed(self, widget):
  1964.         model, rows = widget.get_selection().get_selected_rows()
  1965.         self.btnPDelUser.set_sensitive(bool(rows))
  1966.  
  1967.     # Server side options
  1968.     def on_job_option_reset(self, button):
  1969.         option = self.job_options_buttons[button]
  1970.         option.reset ()
  1971.         # Remember to set this option for removal in the IPP request.
  1972.         if self.server_side_options.has_key (option.name):
  1973.             del self.server_side_options[option.name]
  1974.         if option.is_changed ():
  1975.             self.changed.add(option)
  1976.         else:
  1977.             self.changed.discard(option)
  1978.         self.setDataButtonState()
  1979.  
  1980.     def on_job_option_changed(self, widget):
  1981.         if not self.printer:
  1982.             return
  1983.         option = self.job_options_widgets[widget]
  1984.         option.changed ()
  1985.         if option.is_changed ():
  1986.             self.server_side_options[option.name] = option
  1987.             self.changed.add(option)
  1988.         else:
  1989.             if self.server_side_options.has_key (option.name):
  1990.                 del self.server_side_options[option.name]
  1991.             self.changed.discard(option)
  1992.         self.setDataButtonState()
  1993.         # Don't set the reset button insensitive if the option hasn't
  1994.         # changed from the original value: it's still meaningful to
  1995.         # reset the option to the system default.
  1996.  
  1997.     def draw_other_job_options (self, editable=True):
  1998.         n = len (self.other_job_options)
  1999.         if n == 0:
  2000.             self.tblJOOther.hide_all ()
  2001.             return
  2002.  
  2003.         self.tblJOOther.resize (n, 3)
  2004.         children = self.tblJOOther.get_children ()
  2005.         for child in children:
  2006.             self.tblJOOther.remove (child)
  2007.         i = 0
  2008.         for opt in self.other_job_options:
  2009.             self.tblJOOther.attach (opt.label, 0, 1, i, i + 1,
  2010.                                     xoptions=gtk.FILL,
  2011.                                     yoptions=gtk.FILL)
  2012.             opt.label.set_alignment (0.0, 0.5)
  2013.             self.tblJOOther.attach (opt.selector, 1, 2, i, i + 1,
  2014.                                     xoptions=gtk.FILL,
  2015.                                     yoptions=0)
  2016.             opt.selector.set_sensitive (editable)
  2017.  
  2018.             btn = gtk.Button(stock=gtk.STOCK_REMOVE)
  2019.             btn.connect("clicked", self.on_btnJOOtherRemove_clicked)
  2020.             btn.set_data("pyobject", opt)
  2021.             btn.set_sensitive (editable)
  2022.             self.tblJOOther.attach(btn, 2, 3, i, i + 1,
  2023.                                    xoptions=0,
  2024.                                    yoptions=0)
  2025.             i += 1
  2026.  
  2027.         self.tblJOOther.show_all ()
  2028.  
  2029.     def add_job_option(self, name, value = "", supported = "", is_new=True,
  2030.                        editable=True):
  2031.         option = options.OptionWidget(name, value, supported,
  2032.                                       self.option_changed)
  2033.         option.is_new = is_new
  2034.         self.other_job_options.append (option)
  2035.         self.draw_other_job_options (editable=editable)
  2036.         self.server_side_options[name] = option
  2037.         if name in self.changed: # was deleted before
  2038.             option.is_new = False
  2039.         self.changed.add(option)
  2040.         self.setDataButtonState()
  2041.         if is_new:
  2042.             option.selector.grab_focus ()
  2043.  
  2044.     def on_btnJOOtherRemove_clicked(self, button):
  2045.         option = button.get_data("pyobject")
  2046.         self.other_job_options.remove (option)
  2047.         self.draw_other_job_options ()
  2048.         if option.is_new:
  2049.             self.changed.discard(option)
  2050.         else:
  2051.             # keep name as reminder that option got deleted
  2052.             self.changed.add(option.name)
  2053.         del self.server_side_options[option.name]
  2054.         self.setDataButtonState()
  2055.  
  2056.     def on_btnNewJobOption_clicked(self, button):
  2057.         name = self.entNewJobOption.get_text()
  2058.         self.add_job_option(name)
  2059.         self.tblJOOther.show_all()
  2060.         self.entNewJobOption.set_text ('')
  2061.         self.btnNewJobOption.set_sensitive (False)
  2062.         self.setDataButtonState()
  2063.  
  2064.     def on_entNewJobOption_changed(self, widget):
  2065.         text = self.entNewJobOption.get_text()
  2066.         active = (len(text) > 0) and text not in self.server_side_options
  2067.         self.btnNewJobOption.set_sensitive(active)
  2068.  
  2069.     def on_entNewJobOption_activate(self, widget):
  2070.         self.on_btnNewJobOption_clicked (widget) # wrong widget but ok
  2071.  
  2072.     # set buttons sensitivity
  2073.     def setDataButtonState(self):
  2074.         try:
  2075.             printable = (self.ppd != None and
  2076.                          not bool (self.changed) and
  2077.                          self.printer.enabled and
  2078.                          not self.printer.rejecting)
  2079.  
  2080.             self.btnPrintTestPage.set_sensitive (printable)
  2081.             adjustable = not (self.printer.discovered or bool (self.changed))
  2082.             for button in [self.btnChangePPD,
  2083.                            self.btnSelectDevice]:
  2084.                 button.set_sensitive (adjustable)
  2085.  
  2086.             commands = (self.printer.type & cups.CUPS_PRINTER_COMMANDS) != 0
  2087.             self.btnSelfTest.set_sensitive (commands and printable)
  2088.             self.btnCleanHeads.set_sensitive (commands and printable)
  2089.         except:
  2090.             nonfatalException()
  2091.  
  2092.         installablebold = False
  2093.         optionsbold = False
  2094.         if self.conflicts:
  2095.             debugprint ("Conflicts detected")
  2096.             self.btnConflict.show()
  2097.             for option in self.conflicts:
  2098.                 if option.tab_label == self.lblPInstallOptions:
  2099.                     installablebold = True
  2100.                 else:
  2101.                     optionsbold = True
  2102.         else:
  2103.             self.btnConflict.hide()
  2104.         installabletext = _("Installable Options")
  2105.         optionstext = _("Printer Options")
  2106.         if installablebold:
  2107.             installabletext = "<b>%s</b>" % installabletext
  2108.         if optionsbold:
  2109.             optionstext = "<b>%s</b>" % optionstext
  2110.         self.lblPInstallOptions.set_markup (installabletext)
  2111.         self.lblPOptions.set_markup (optionstext)
  2112.  
  2113.         store = self.tvPrinterProperties.get_model ()
  2114.         if store:
  2115.             for n in range (self.ntbkPrinter.get_n_pages ()):
  2116.                 page = self.ntbkPrinter.get_nth_page (n)
  2117.                 label = self.ntbkPrinter.get_tab_label (page)
  2118.                 try:
  2119.                     if label == self.lblPInstallOptions:
  2120.                         iter = store.get_iter ((n,))
  2121.                         store.set_value (iter, 0, installabletext)
  2122.                     elif label == self.lblPOptions:
  2123.                         iter = store.get_iter ((n,))
  2124.                         store.set_value (iter, 0, optionstext)
  2125.                 except ValueError:
  2126.                     # If we get here, the store has not yet been set
  2127.                     # up (trac #111).
  2128.                     pass
  2129.  
  2130.         self.btnPrinterPropertiesApply.set_sensitive (len (self.changed) > 0 and
  2131.                                                       not self.conflicts)
  2132.         self.btnPrinterPropertiesOK.set_sensitive (len (self.changed) > 0 and
  2133.                                                    not self.conflicts)
  2134.  
  2135.     def save_printer(self, printer, saveall=False, parent=None):
  2136.         if parent == None:
  2137.             parent = self.PrinterPropertiesDialog
  2138.         class_deleted = False
  2139.         name = printer.name
  2140.  
  2141.         if printer.is_class:
  2142.             self.cups._begin_operation (_("modifying class %s") % name)
  2143.         else:
  2144.             self.cups._begin_operation (_("modifying printer %s") % name)
  2145.  
  2146.         try:
  2147.             if not printer.is_class and self.ppd:
  2148.                 self.getPrinterSettings()
  2149.                 if self.ppd.nondefaultsMarked() or saveall:
  2150.                     self.cups.addPrinter(name, ppd=self.ppd)
  2151.  
  2152.             if printer.is_class:
  2153.                 # update member list
  2154.                 new_members = getCurrentClassMembers(self.tvClassMembers)
  2155.                 if not new_members:
  2156.                     dialog = gtk.MessageDialog(
  2157.                         flags=0, type=gtk.MESSAGE_WARNING,
  2158.                         buttons=gtk.BUTTONS_NONE,
  2159.                         message_format=_("This will delete this class!"))
  2160.                     dialog.format_secondary_text(_("Proceed anyway?"))
  2161.                     dialog.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
  2162.                                         gtk.STOCK_DELETE, gtk.RESPONSE_YES)
  2163.                     result = dialog.run()
  2164.                     dialog.destroy()
  2165.                     if result==gtk.RESPONSE_NO:
  2166.                         self.cups._end_operation ()
  2167.                         return True
  2168.                     class_deleted = True
  2169.  
  2170.                 # update member list
  2171.                 old_members = printer.class_members[:]
  2172.  
  2173.                 for member in new_members:
  2174.                     if member in old_members:
  2175.                         old_members.remove(member)
  2176.                     else:
  2177.                         self.cups.addPrinterToClass(member, name)
  2178.                 for member in old_members:
  2179.                     self.cups.deletePrinterFromClass(member, name)
  2180.  
  2181.             location = self.entPLocation.get_text()
  2182.             info = self.entPDescription.get_text()
  2183.             device_uri = self.entPDevice.get_text()
  2184.  
  2185.             enabled = self.chkPEnabled.get_active()
  2186.             accepting = self.chkPAccepting.get_active()
  2187.             shared = self.chkPShared.get_active()
  2188.  
  2189.             if info!=printer.info or saveall:
  2190.                 self.cups.setPrinterInfo(name, info)
  2191.             if location!=printer.location or saveall:
  2192.                 self.cups.setPrinterLocation(name, location)
  2193.             if (not printer.is_class and
  2194.                 (device_uri!=printer.device_uri or saveall)):
  2195.                 self.cups.setPrinterDevice(name, device_uri)
  2196.  
  2197.             if enabled != printer.enabled or saveall:
  2198.                 self.printer.setEnabled(enabled)
  2199.             if accepting == printer.rejecting or saveall:
  2200.                 self.printer.setAccepting(accepting)
  2201.             if shared != printer.is_shared or saveall:
  2202.                 self.printer.setShared(shared)
  2203.  
  2204.             def get_combo_value (cmb):
  2205.                 model = cmb.get_model ()
  2206.                 iter = cmb.get_active_iter ()
  2207.                 return model.get_value (iter, 1)
  2208.  
  2209.             job_sheet_start = get_combo_value (self.cmbPStartBanner)
  2210.             job_sheet_end = get_combo_value (self.cmbPEndBanner)
  2211.             error_policy = get_combo_value (self.cmbPErrorPolicy)
  2212.             op_policy = get_combo_value (self.cmbPOperationPolicy)
  2213.  
  2214.             if (job_sheet_start != printer.job_sheet_start or
  2215.                 job_sheet_end != printer.job_sheet_end) or saveall:
  2216.                 printer.setJobSheets(job_sheet_start, job_sheet_end)
  2217.             if error_policy != printer.error_policy or saveall:
  2218.                 printer.setErrorPolicy(error_policy)
  2219.             if op_policy != printer.op_policy or saveall:
  2220.                 printer.setOperationPolicy(op_policy)
  2221.  
  2222.             default_allow = self.rbtnPAllow.get_active()
  2223.             except_users = self.getPUsers()
  2224.  
  2225.             if (default_allow != printer.default_allow or
  2226.                 except_users != printer.except_users) or saveall:
  2227.                 printer.setAccess(default_allow, except_users)
  2228.  
  2229.             for option in printer.attributes:
  2230.                 if option not in self.server_side_options:
  2231.                     printer.unsetOption(option)
  2232.             for option in self.server_side_options.itervalues():
  2233.                 if (option.is_changed() or
  2234.                     (saveall and
  2235.                      option.get_current_value () != option.get_default())):
  2236.                     printer.setOption(option.name, option.get_current_value())
  2237.  
  2238.         except cups.IPPError, (e, s):
  2239.             show_IPP_Error(e, s, parent)
  2240.             self.cups._end_operation ()
  2241.             return True
  2242.         self.cups._end_operation ()
  2243.         self.changed = set() # of options
  2244.  
  2245.         if not self.cups._use_pk and not self.__dict__.has_key ("server_settings"):
  2246.             # We can authenticate with the server correctly at this point,
  2247.             # but we have never fetched the server settings to see whether
  2248.             # the server is publishing shared printers.  Fetch the settings
  2249.             # now so that we can update the "not published" label if necessary.
  2250.             self.cups._begin_operation (_("fetching server settings"))
  2251.             try:
  2252.                 self.server_settings = self.cups.adminGetServerSettings()
  2253.             except:
  2254.                 nonfatalException()
  2255.  
  2256.             self.cups._end_operation ()
  2257.  
  2258.         if class_deleted:
  2259.             self.monitor.update ()
  2260.         else:
  2261.             # Update our copy of the printer's settings.
  2262.             self.cups._begin_operation (_("obtaining queue details"))
  2263.             try:
  2264.                 printers = cupshelpers.getPrinters (self.cups)
  2265.                 this_printer = { name: printers[name] }
  2266.                 self.printers.update (this_printer)
  2267.             except cups.IPPError, (e, s):
  2268.                 show_IPP_Error(e, s, self.PrinterPropertiesDialog)
  2269.             except KeyError:
  2270.                 # The printer was deleted in the mean time and the
  2271.                 # user made no changes.
  2272.                 self.populateList ()
  2273.  
  2274.             self.cups._end_operation ()
  2275.         return False
  2276.  
  2277.     def getPrinterSettings(self):
  2278.         #self.ppd.markDefaults()
  2279.         for option in self.options.itervalues():
  2280.             option.writeback()
  2281.  
  2282.     ### Printer Properties tree view signal handlers
  2283.     def on_tvPrinterProperties_selection_changed (self, selection):
  2284.         # Prevent selection from being de-selected.
  2285.         (model, iter) = selection.get_selected ()
  2286.         if iter:
  2287.             self.printer_properties_last_iter_selected = iter
  2288.         else:
  2289.             try:
  2290.                 iter = self.printer_properties_last_iter_selected
  2291.             except AttributeError:
  2292.                 # Not set yet.
  2293.                 return
  2294.  
  2295.             if model.iter_is_valid (iter):
  2296.                 selection.select_iter (iter)
  2297.  
  2298.     def on_tvPrinterProperties_cursor_changed (self, treeview):
  2299.         # Adjust notebook to reflect selected item.
  2300.         (path, column) = treeview.get_cursor ()
  2301.         if path != None:
  2302.             model = treeview.get_model ()
  2303.             iter = model.get_iter (path)
  2304.             n = model.get_value (iter, 1)
  2305.             self.ntbkPrinter.set_current_page (n)
  2306.  
  2307.     # set default printer
  2308.     def set_system_or_user_default_printer (self, name):
  2309.         # First, decide if this is already the system default, in which
  2310.         # case we only need to clear the user default.
  2311.         userdef = userdefault.UserDefaultPrinter ()
  2312.         if name == self.default_printer:
  2313.             userdef.clear ()
  2314.             self.populateList ()
  2315.             return
  2316.  
  2317.         userdefault.UserDefaultPrompt (self.set_default_printer,
  2318.                                        self.populateList,
  2319.                                        name,
  2320.                                        _("Set Default Printer"),
  2321.                                        self.PrintersWindow,
  2322.                                        _("Do you want to set this as "
  2323.                                          "the system-wide default printer?"),
  2324.                                        _("Set as the _system-wide "
  2325.                                          "default printer"),
  2326.                                        _("_Clear my personal default setting"),
  2327.                                        _("Set as my _personal default printer"))
  2328.  
  2329.     def set_default_printer (self, name):
  2330.         printer = self.printers[name]
  2331.         reload = False
  2332.         self.cups._begin_operation (_("setting default printer"))
  2333.         try:
  2334.             reload = printer.setAsDefault ()
  2335.         except cups.HTTPError, (s,):
  2336.             show_HTTP_Error (s, self.PrintersWindow)
  2337.             self.cups._end_operation ()
  2338.             return
  2339.         except cups.IPPError, (e, msg):
  2340.             show_IPP_Error(e, msg, self.PrintersWindow)
  2341.             self.cups._end_operation ()
  2342.             return
  2343.  
  2344.         self.cups._end_operation ()
  2345.  
  2346.         # Now reconnect in case the server needed to reload.  This may
  2347.         # happen if we replaced the lpoptions file.
  2348.         if reload:
  2349.             self.reconnect ()
  2350.  
  2351.         try:
  2352.             self.populateList()
  2353.         except cups.HTTPError, (s,):
  2354.             self.cups = None
  2355.             self.setConnected()
  2356.             self.populateList()
  2357.             show_HTTP_Error(s, self.PrintersWindow)
  2358.  
  2359.     # print test page
  2360.  
  2361.     def on_btnPrintTestPage_clicked(self, button):
  2362.         if self.ppd == False:
  2363.             # Can't print a test page for a raw queue.
  2364.             return
  2365.  
  2366.         # if we have a page size specific custom test page, use it;
  2367.         # otherwise use cups' default one
  2368.         custom_testpage = None
  2369.         if self.ppd != False:
  2370.             opt = self.ppd.findOption ("PageSize")
  2371.             if opt:
  2372.                 custom_testpage = os.path.join(pkgdata,
  2373.                                                'testpage-%s.ps' %
  2374.                                                opt.defchoice.lower())
  2375.  
  2376.         # Connect as the current user so that the test page can be managed
  2377.         # as a normal job.
  2378.         user = cups.getUser ()
  2379.         cups.setUser ('')
  2380.         try:
  2381.             c = authconn.Connection (self.PrintersWindow, try_as_root=False,
  2382.                                      host=self.connect_server,
  2383.                                      encryption=self.connect_encrypt)
  2384.         except RuntimeError, s:
  2385.             show_IPP_Error (None, s, self.PrintersWindow)
  2386.             return
  2387.  
  2388.         job_id = None
  2389.         c._begin_operation (_("printing test page"))
  2390.         try:
  2391.             if custom_testpage and os.path.exists(custom_testpage):
  2392.                 debugprint ('Printing custom test page ' + custom_testpage)
  2393.                 job_id = c.printTestPage(self.printer.name,
  2394.                                          file=custom_testpage)
  2395.             else:
  2396.                 debugprint ('Printing default test page')
  2397.                 job_id = c.printTestPage(self.printer.name)
  2398.         except cups.IPPError, (e, msg):
  2399.             if (e == cups.IPP_NOT_AUTHORIZED and
  2400.                 self.connect_server != 'localhost' and
  2401.                 self.connect_server[0] != '/'):
  2402.                 show_error_dialog (_("Not possible"),
  2403.                                    _("The remote server did not accept "
  2404.                                      "the print job, most likely "
  2405.                                      "because the printer is not "
  2406.                                      "shared."),
  2407.                                    self.PrintersWindow)
  2408.             else:
  2409.                 show_IPP_Error(e, msg, self.PrintersWindow)
  2410.  
  2411.         c._end_operation ()
  2412.         cups.setUser (user)
  2413.  
  2414.         if job_id != None:
  2415.             show_info_dialog (_("Submitted"),
  2416.                               _("Test page submitted as job %d") % job_id,
  2417.                               parent=self.PrintersWindow)
  2418.  
  2419.     def maintenance_command (self, command):
  2420.         (tmpfd, tmpfname) = tempfile.mkstemp ()
  2421.         os.write (tmpfd, "#CUPS-COMMAND\n%s\n" % command)
  2422.         os.close (tmpfd)
  2423.         self.cups._begin_operation (_("sending maintenance command"))
  2424.         try:
  2425.             format = "application/vnd.cups-command"
  2426.             job_id = self.cups.printTestPage (self.printer.name,
  2427.                                               format=format,
  2428.                                               file=tmpfname,
  2429.                                               user=self.connect_user)
  2430.             show_info_dialog (_("Submitted"),
  2431.                               _("Maintenance command submitted as "
  2432.                                 "job %d") % job_id,
  2433.                               parent=self.PrintersWindow)
  2434.         except cups.IPPError, (e, msg):
  2435.             if (e == cups.IPP_NOT_AUTHORIZED and
  2436.                 self.printer.name != 'localhost'):
  2437.                 show_error_dialog (_("Not possible"),
  2438.                                    _("The remote server did not accept "
  2439.                                      "the print job, most likely "
  2440.                                      "because the printer is not "
  2441.                                      "shared."),
  2442.                                    self.PrintersWindow)
  2443.             else:
  2444.                 show_IPP_Error(e, msg, self.PrintersWindow)
  2445.  
  2446.         self.cups._end_operation ()
  2447.  
  2448.         os.unlink (tmpfname)
  2449.  
  2450.     def on_btnSelfTest_clicked(self, button):
  2451.         self.maintenance_command ("PrintSelfTestPage")
  2452.  
  2453.     def on_btnCleanHeads_clicked(self, button):
  2454.         self.maintenance_command ("Clean all")
  2455.  
  2456.     def fillComboBox(self, combobox, values, value, translationdict=None):
  2457.         if translationdict == None:
  2458.             translationdict = ppdippstr.TranslationDict ()
  2459.  
  2460.         model = gtk.ListStore (gobject.TYPE_STRING,
  2461.                                gobject.TYPE_STRING)
  2462.         combobox.set_model (model)
  2463.         set_active = False
  2464.         for nr, val in enumerate(values):
  2465.             model.append ([(translationdict.get (val)), val])
  2466.             if val == value:
  2467.                 combobox.set_active(nr)
  2468.                 set_active = True
  2469.  
  2470.         if not set_active:
  2471.             combobox.set_active (0)
  2472.  
  2473.     def fillPrinterTab(self, name):
  2474.         self.changed = set() # of options
  2475.         self.options = {} # keyword -> Option object
  2476.         self.conflicts = set() # of options
  2477.  
  2478.         printer = self.printers[name]
  2479.         self.printer = printer
  2480.         printer.getAttributes ()
  2481.         try:
  2482.             # CUPS 1.4
  2483.             publishing = printer.other_attributes['server-is-sharing-printers']
  2484.             self.server_is_publishing = publishing
  2485.         except KeyError:
  2486.             pass
  2487.  
  2488.         editable = not self.printer.discovered
  2489.  
  2490.         try:
  2491.             self.ppd = printer.getPPD()
  2492.             self.ppd_local = printer.getPPD()
  2493.             if self.ppd_local != False:
  2494.                 self.ppd_local.localize()
  2495.         except cups.IPPError, (e, m):
  2496.             # We might get IPP_INTERNAL_ERROR if this is a memberless
  2497.             # class.
  2498.             if e != cups.IPP_INTERNAL_ERROR:
  2499.                 # Some IPP error other than IPP_NOT_FOUND.
  2500.                 show_IPP_Error(e, m, self.PrintersWindow)
  2501.  
  2502.             # Treat it as a raw queue.
  2503.             self.ppd = False
  2504.         except RuntimeError:
  2505.             # The underlying cupsGetPPD2() function returned NULL without
  2506.             # setting an IPP error, so it'll be something like a failed
  2507.             # connection.
  2508.             show_error_dialog (_("Error"),
  2509.                                _("There was a problem connecting to "
  2510.                                  "the CUPS server."),
  2511.                                self.PrintersWindow)
  2512.             raise
  2513.  
  2514.         for widget in (self.entPDescription, self.entPLocation,
  2515.                        self.entPDevice):
  2516.             widget.set_editable(editable)
  2517.  
  2518.         for widget in (self.btnSelectDevice, self.btnChangePPD,
  2519.                        self.chkPEnabled, self.chkPAccepting, self.chkPShared,
  2520.                        self.cmbPStartBanner, self.cmbPEndBanner,
  2521.                        self.cmbPErrorPolicy, self.cmbPOperationPolicy,
  2522.                        self.rbtnPAllow, self.rbtnPDeny, self.tvPUsers,
  2523.                        self.entPUser, self.btnPAddUser, self.btnPDelUser):
  2524.             widget.set_sensitive(editable)
  2525.  
  2526.         # Description page
  2527.         self.entPDescription.set_text(printer.info)
  2528.         self.entPLocation.set_text(printer.location)
  2529.  
  2530.         uri = printer.device_uri
  2531.         self.entPDevice.set_text(uri)
  2532.         self.changed.discard(self.entPDevice)
  2533.  
  2534.         # Hide make/model and Device URI for classes
  2535.         for widget in (self.lblPMakeModel2, self.lblPMakeModel,
  2536.                        self.btnChangePPD, self.lblPDevice2,
  2537.                        self.entPDevice, self.btnSelectDevice):
  2538.             if printer.is_class:
  2539.                 widget.hide()
  2540.             else:
  2541.                 widget.show()
  2542.  
  2543.  
  2544.         # Policy tab
  2545.         # ----------
  2546.  
  2547.         try:
  2548.             if printer.is_shared:
  2549.                 if self.server_is_publishing:
  2550.                     self.lblNotPublished.hide_all ()
  2551.                 else:
  2552.                     self.lblNotPublished.show_all ()
  2553.             else:
  2554.                 self.lblNotPublished.hide_all ()
  2555.         except:
  2556.             nonfatalException()
  2557.             self.lblNotPublished.hide_all ()
  2558.  
  2559.         # Job sheets
  2560.         self.cmbPStartBanner.set_sensitive(editable)
  2561.         self.cmbPEndBanner.set_sensitive(editable)
  2562.  
  2563.         # Policies
  2564.         self.cmbPErrorPolicy.set_sensitive(editable)
  2565.         self.cmbPOperationPolicy.set_sensitive(editable)
  2566.  
  2567.         # Access control
  2568.         self.entPUser.set_text("")
  2569.  
  2570.         # Server side options (Job options)
  2571.         self.server_side_options = {}
  2572.         for option in self.job_options_widgets.values ():
  2573.             if option.name == "media" and self.ppd:
  2574.                 # Slightly special case because the 'system default'
  2575.                 # (i.e. what you get when you press Reset) depends
  2576.                 # on the printer's PageSize.
  2577.                 opt = self.ppd.findOption ("PageSize")
  2578.                 if opt:
  2579.                     option.set_default (opt.defchoice)
  2580.  
  2581.             option_editable = editable
  2582.             try:
  2583.                 value = self.printer.attributes[option.name]
  2584.             except KeyError:
  2585.                 option.reinit (None)
  2586.             else:
  2587.                 try:
  2588.                     if self.printer.possible_attributes.has_key (option.name):
  2589.                         supported = self.printer.\
  2590.                                     possible_attributes[option.name][1]
  2591.                         # Set the option widget.
  2592.                         # In CUPS 1.3.x the orientation-requested-default
  2593.                         # attribute may have the value None; this means there
  2594.                         # is no value set.  This suits our needs here, as None
  2595.                         # resets the option to the system default and makes the
  2596.                         # Reset button insensitive.
  2597.                         option.reinit (value, supported=supported)
  2598.                     else:
  2599.                         option.reinit (value)
  2600.  
  2601.                     self.server_side_options[option.name] = option
  2602.                 except:
  2603.                     option_editable = False
  2604.                     show_error_dialog (_("Error"),
  2605.                                        _("Option '%s' has value '%s' "
  2606.                                          "and cannot be edited.") %
  2607.                                        (option.name, value),
  2608.                                        self.PrintersWindow)
  2609.             option.widget.set_sensitive (option_editable)
  2610.             if not editable:
  2611.                 option.button.set_sensitive (False)
  2612.         self.other_job_options = []
  2613.         self.draw_other_job_options (editable=editable)
  2614.         for option in self.printer.attributes.keys ():
  2615.             if self.server_side_options.has_key (option):
  2616.                 continue
  2617.             value = self.printer.attributes[option]
  2618.             if self.printer.possible_attributes.has_key (option):
  2619.                 supported = self.printer.possible_attributes[option][1]
  2620.             else:
  2621.                 if isinstance (value, bool):
  2622.                     supported = ["true", "false"]
  2623.                     value = str (value).lower ()
  2624.                 else:
  2625.                     supported = ""
  2626.                     value = str (value)
  2627.  
  2628.             self.add_job_option (option, value=value,
  2629.                                  supported=supported, is_new=False,
  2630.                                  editable=editable)
  2631.         self.entNewJobOption.set_text ('')
  2632.         self.entNewJobOption.set_sensitive (editable)
  2633.         self.btnNewJobOption.set_sensitive (False)
  2634.  
  2635.         if printer.is_class:
  2636.             # remove InstallOptions tab
  2637.             tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2638.             if tab_nr != -1:
  2639.                 self.ntbkPrinter.remove_page(tab_nr)
  2640.             self.fillClassMembers(name, editable)
  2641.         else:
  2642.             # real Printer
  2643.             self.fillPrinterOptions(name, editable)
  2644.  
  2645.         self.updateMarkerLevels()
  2646.         self.updateStateReasons()
  2647.         self.updatePrinterPropertiesTreeView()
  2648.  
  2649.         self.changed = set() # of options
  2650.         self.updatePrinterProperties ()
  2651.         self.setDataButtonState()
  2652.  
  2653.     def updatePrinterPropertiesTreeView (self):
  2654.         # Now update the tree view (which we use instead of the notebook tabs).
  2655.         store = gtk.ListStore (gobject.TYPE_STRING, gobject.TYPE_INT)
  2656.         self.ntbkPrinter.set_show_tabs (False)
  2657.         for n in range (self.ntbkPrinter.get_n_pages ()):
  2658.             page = self.ntbkPrinter.get_nth_page (n)
  2659.             label = self.ntbkPrinter.get_tab_label (page)
  2660.             iter = store.append (None)
  2661.             store.set_value (iter, 0, label.get_text ())
  2662.             store.set_value (iter, 1, n)
  2663.         sel = self.tvPrinterProperties.get_selection ()
  2664.         self.tvPrinterProperties.set_model (store)
  2665.  
  2666.     def updateMarkerLevels (self):
  2667.         printer = self.printer
  2668.  
  2669.         # Marker levels
  2670.         for widget in self.vboxMarkerLevels.get_children ():
  2671.             self.vboxMarkerLevels.remove (widget)
  2672.  
  2673.         marker_info = dict()
  2674.         num_markers = 0
  2675.         for (attr, typ) in [('marker-colors', str),
  2676.                             ('marker-names', str),
  2677.                             ('marker-types', str),
  2678.                             ('marker-levels', float)]:
  2679.             val = printer.other_attributes.get (attr, [])
  2680.             if typ != str and len (val) > 0:
  2681.                 try:
  2682.                     # Can the value be coerced into the right type?
  2683.                     typ (val[0])
  2684.                 except TypeError, s:
  2685.                     debugprint ("%s value not coercible to %s: %s" %
  2686.                                 (attr, typ, s))
  2687.                     val = map (lambda x: 0.0, val)
  2688.  
  2689.             marker_info[attr] = val
  2690.             if num_markers == 0 or len (val) < num_markers:
  2691.                 num_markers = len (val)
  2692.  
  2693.         for attr in ['marker-colors', 'marker-names',
  2694.                      'marker-types', 'marker-levels']:
  2695.             if len (marker_info[attr]) > num_markers:
  2696.                 debugprint ("Trimming %s from %s" %
  2697.                             (marker_info[attr][num_markers:], attr))
  2698.                 del marker_info[attr][num_markers:]
  2699.  
  2700.         markers = map (lambda color, name, type, level:
  2701.                            (color, name, type, level),
  2702.                        marker_info['marker-colors'],
  2703.                        marker_info['marker-names'],
  2704.                        marker_info['marker-types'],
  2705.                        marker_info['marker-levels'])
  2706.         debugprint (markers)
  2707.  
  2708.         can_refresh = (self.printer.type & cups.CUPS_PRINTER_COMMANDS) != 0
  2709.         self.btnRefreshMarkerLevels.set_sensitive (can_refresh)
  2710.         if len (markers) == 0:
  2711.             label = gtk.Label(_("Marker levels are not reported "
  2712.                                 "for this printer."))
  2713.             label.set_line_wrap (True)
  2714.             label.set_alignment (0.0, 0.0)
  2715.             self.vboxMarkerLevels.pack_start (label, False, False, 0)
  2716.         else:
  2717.             num_markers = 0
  2718.             cols = len (markers)
  2719.             rows = 1 + (cols - 1) / 4
  2720.             if cols > 4:
  2721.                 cols = 4
  2722.             table = gtk.Table (rows=rows,
  2723.                                columns=cols,
  2724.                                homogeneous=True)
  2725.             table.set_col_spacings (6)
  2726.             table.set_row_spacings (12)
  2727.             self.vboxMarkerLevels.pack_start (table)
  2728.             for color, name, marker_type, level in markers:
  2729.                 if name == None:
  2730.                     name = ''
  2731.                 else:
  2732.                     ppd = printer.getPPD()
  2733.                     if ppd != False:
  2734.                         localized_name = ppd.localizeMarkerName(name)
  2735.                         if localized_name != None:
  2736.                             name = localized_name
  2737.  
  2738.                 row = num_markers / 4
  2739.                 col = num_markers % 4
  2740.  
  2741.                 vbox = gtk.VBox (spacing=6)
  2742.                 subhbox = gtk.HBox ()
  2743.                 inklevel = gtkinklevel.GtkInkLevel (color, level)
  2744.                 inklevel.set_tooltip_text ("%d%%" % level)
  2745.                 subhbox.pack_start (inklevel, True, False, 0)
  2746.                 vbox.pack_start (subhbox, False, False, 0)
  2747.                 label = gtk.Label (name)
  2748.                 label.set_width_chars (10)
  2749.                 label.set_line_wrap (True)
  2750.                 vbox.pack_start (label, False, False, 0)
  2751.                 table.attach (vbox, col, col + 1, row, row + 1)
  2752.                 num_markers += 1
  2753.  
  2754.         self.vboxMarkerLevels.show_all ()
  2755.  
  2756.     def on_btnRefreshMarkerLevels_clicked (self, button):
  2757.         self.maintenance_command ("ReportLevels")
  2758.  
  2759.     def updateStateReasons (self):
  2760.         printer = self.printer
  2761.         reasons = printer.other_attributes.get ('printer-state-reasons', [])
  2762.         store = gtk.ListStore (str, str)
  2763.         any = False
  2764.         for reason in reasons:
  2765.             if reason == "none":
  2766.                 break
  2767.  
  2768.             any = True
  2769.             iter = store.append (None)
  2770.             r = statereason.StateReason (printer.connection, printer.name, reason)
  2771.             if r.get_reason () == "paused":
  2772.                 icon = gtk.STOCK_MEDIA_PAUSE
  2773.             else:
  2774.                 icon = statereason.StateReason.LEVEL_ICON[r.get_level ()]
  2775.             store.set_value (iter, 0, icon)
  2776.             (title, text) = r.get_description ()
  2777.             store.set_value (iter, 1, text)
  2778.  
  2779.         self.tvPrinterStateReasons.set_model (store)
  2780.         page = 0
  2781.         if any:
  2782.             page = 1
  2783.  
  2784.         self.ntbkPrinterStateReasons.set_current_page (page)
  2785.  
  2786.     def set_printer_state_reason_icon (self, column, cell, model, iter, *data):
  2787.         icon = model.get_value (iter, 0)
  2788.         theme = gtk.icon_theme_get_default ()
  2789.         try:
  2790.             pixbuf = theme.load_icon (icon, 22, 0)
  2791.             cell.set_property ("pixbuf", pixbuf)
  2792.         except gobject.GError, exc:
  2793.             pass # Couldn't load icon
  2794.  
  2795.     def set_printer_state_reason_text (self, column, cell, model, iter, *data):
  2796.         cell.set_property ("text", model.get_value (iter, 1))
  2797.  
  2798.     def updatePrinterProperties(self):
  2799.         debugprint ("update printer properties")
  2800.         printer = self.printer
  2801.         self.lblPMakeModel.set_text(printer.make_and_model)
  2802.         state = self.printer_states.get (printer.state, _("Unknown"))
  2803.         reason = printer.other_attributes.get ('printer-state-message', '')
  2804.         if len (reason) > 0:
  2805.             state += ' - ' + reason
  2806.         self.lblPState.set_text(state)
  2807.         if len (self.changed) == 0:
  2808.             debugprint ("no changes yet: full printer properties update")
  2809.             # State
  2810.             self.chkPEnabled.set_active(printer.enabled)
  2811.             self.chkPAccepting.set_active(not printer.rejecting)
  2812.             self.chkPShared.set_active(printer.is_shared)
  2813.  
  2814.             # Job sheets
  2815.             self.fillComboBox(self.cmbPStartBanner,
  2816.                               printer.job_sheets_supported,
  2817.                               printer.job_sheet_start,
  2818.                               ppdippstr.job_sheets)
  2819.             self.fillComboBox(self.cmbPEndBanner, printer.job_sheets_supported,
  2820.                               printer.job_sheet_end,
  2821.                               ppdippstr.job_sheets)
  2822.  
  2823.             # Policies
  2824.             self.fillComboBox(self.cmbPErrorPolicy,
  2825.                               printer.error_policy_supported,
  2826.                               printer.error_policy,
  2827.                               ppdippstr.printer_error_policy)
  2828.             self.fillComboBox(self.cmbPOperationPolicy,
  2829.                               printer.op_policy_supported,
  2830.                               printer.op_policy,
  2831.                               ppdippstr.printer_op_policy)
  2832.  
  2833.             # Access control
  2834.             self.rbtnPAllow.set_active(printer.default_allow)
  2835.             self.rbtnPDeny.set_active(not printer.default_allow)
  2836.             self.setPUsers(printer.except_users)
  2837.  
  2838.             # Marker levels
  2839.             self.updateMarkerLevels ()
  2840.             self.updateStateReasons ()
  2841.  
  2842.             self.updatePrinterPropertiesTreeView ()
  2843.  
  2844.     def fillPrinterOptions(self, name, editable):
  2845.         # remove Class membership tab
  2846.         tab_nr = self.ntbkPrinter.page_num(self.algnClassMembers)
  2847.         if tab_nr != -1:
  2848.             self.ntbkPrinter.remove_page(tab_nr)
  2849.  
  2850.         # clean Installable Options Tab
  2851.         for widget in self.vbPInstallOptions.get_children():
  2852.             self.vbPInstallOptions.remove(widget)
  2853.  
  2854.         # clean Options Tab
  2855.         for widget in self.vbPOptions.get_children():
  2856.             self.vbPOptions.remove(widget)
  2857.  
  2858.         # insert Options Tab
  2859.         if self.ntbkPrinter.page_num(self.swPOptions) == -1:
  2860.             self.ntbkPrinter.insert_page(
  2861.                 self.swPOptions, self.lblPOptions, self.static_tabs)
  2862.  
  2863.         if not self.ppd:
  2864.             tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2865.             if tab_nr != -1:
  2866.                 self.ntbkPrinter.remove_page(tab_nr)
  2867.             tab_nr = self.ntbkPrinter.page_num(self.swPOptions)
  2868.             if tab_nr != -1:
  2869.                 self.ntbkPrinter.remove_page(tab_nr)
  2870.             return
  2871.         ppd = self.ppd
  2872.         ppd.markDefaults()
  2873.         self.ppd_local.markDefaults()
  2874.  
  2875.         hasInstallableOptions = False
  2876.  
  2877.         # build option tabs
  2878.         for group in self.ppd_local.optionGroups:
  2879.             if group.name == "InstallableOptions":
  2880.                 hasInstallableOptions = True
  2881.                 container = self.vbPInstallOptions
  2882.                 tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2883.                 if tab_nr == -1:
  2884.                     self.ntbkPrinter.insert_page(self.swPInstallOptions,
  2885.                                                  gtk.Label(group.text),
  2886.                                                  self.static_tabs)
  2887.                 tab_label = self.lblPInstallOptions
  2888.             else:
  2889.                 frame = gtk.Frame("<b>%s</b>" % ppdippstr.ppd.get (group.text))
  2890.                 frame.get_label_widget().set_use_markup(True)
  2891.                 frame.set_shadow_type (gtk.SHADOW_NONE)
  2892.                 self.vbPOptions.pack_start (frame, False, False, 0)
  2893.                 container = gtk.Alignment (0.5, 0.5, 1.0, 1.0)
  2894.                 # We want a left padding of 12, but there is a Table with
  2895.                 # spacing 6, and the left-most column of it (the conflict
  2896.                 # icon) is normally hidden, so just use 6 here.
  2897.                 container.set_padding (6, 12, 6, 0)
  2898.                 frame.add (container)
  2899.                 tab_label = self.lblPOptions
  2900.  
  2901.             table = gtk.Table(1, 3, False)
  2902.             table.set_col_spacings(6)
  2903.             table.set_row_spacings(6)
  2904.             container.add(table)
  2905.  
  2906.             rows = 0
  2907.  
  2908.             # InputSlot and ManualFeed need special handling.  With
  2909.             # libcups, if ManualFeed is True, InputSlot gets unset.
  2910.             # Likewise, if InputSlot is set, ManualFeed becomes False.
  2911.             # We handle it by toggling the sensitivity of InputSlot
  2912.             # based on ManualFeed.
  2913.             self.option_inputslot = self.option_manualfeed = None
  2914.  
  2915.             for nr, option in enumerate(group.options):
  2916.                 if option.keyword == "PageRegion":
  2917.                     continue
  2918.                 rows += 1
  2919.                 table.resize (rows, 3)
  2920.                 o = OptionWidget(option, ppd, self, tab_label=tab_label)
  2921.                 table.attach(o.conflictIcon, 0, 1, nr, nr+1, 0, 0, 0, 0)
  2922.  
  2923.                 hbox = gtk.HBox()
  2924.                 if o.label:
  2925.                     a = gtk.Alignment (0.5, 0.5, 1.0, 1.0)
  2926.                     a.set_padding (0, 0, 0, 6)
  2927.                     a.add (o.label)
  2928.                     table.attach(a, 1, 2, nr, nr+1, gtk.FILL, 0, 0, 0)
  2929.                     table.attach(hbox, 2, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  2930.                 else:
  2931.                     table.attach(hbox, 1, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  2932.                 hbox.pack_start(o.selector, False)
  2933.                 self.options[option.keyword] = o
  2934.                 o.selector.set_sensitive(editable)
  2935.                 if option.keyword == "InputSlot":
  2936.                     self.option_inputslot = o
  2937.                 elif option.keyword == "ManualFeed":
  2938.                     self.option_manualfeed = o
  2939.  
  2940.         # remove Installable Options tab if not needed
  2941.         if not hasInstallableOptions:
  2942.             tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2943.             if tab_nr != -1:
  2944.                 self.ntbkPrinter.remove_page(tab_nr)
  2945.  
  2946.         # check for conflicts
  2947.         for option in self.options.itervalues():
  2948.             conflicts = option.checkConflicts()
  2949.             if conflicts:
  2950.                 self.conflicts.add(option)
  2951.  
  2952.         self.swPInstallOptions.show_all()
  2953.         self.swPOptions.show_all()
  2954.  
  2955.     # Class members
  2956.  
  2957.     def fillClassMembers(self, name, editable):
  2958.         printer = self.printers[name]
  2959.  
  2960.         self.btnClassAddMember.set_sensitive(editable)
  2961.         self.btnClassDelMember.set_sensitive(editable)
  2962.  
  2963.         # remove Options tab
  2964.         tab_nr = self.ntbkPrinter.page_num(self.swPOptions)
  2965.         if tab_nr != -1:
  2966.             self.ntbkPrinter.remove_page(tab_nr)
  2967.  
  2968.         # insert Member Tab
  2969.         if self.ntbkPrinter.page_num(self.algnClassMembers) == -1:
  2970.             self.ntbkPrinter.insert_page(
  2971.                 self.algnClassMembers, self.lblClassMembers,
  2972.                 self.static_tabs)
  2973.  
  2974.         model_members = self.tvClassMembers.get_model()
  2975.         model_not_members = self.tvClassNotMembers.get_model()
  2976.         model_members.clear()
  2977.         model_not_members.clear()
  2978.  
  2979.         names = self.printers.keys()
  2980.         names.sort()
  2981.         for name in names:
  2982.             p = self.printers[name]
  2983.             if p is not printer:
  2984.                 if name in printer.class_members:
  2985.                     model_members.append((name, ))
  2986.                 else:
  2987.                     model_not_members.append((name, ))
  2988.  
  2989.     def on_btnClassAddMember_clicked(self, button):
  2990.         moveClassMembers(self.tvClassNotMembers,
  2991.                          self.tvClassMembers)
  2992.         if getCurrentClassMembers(self.tvClassMembers) != self.printer.class_members:
  2993.             self.changed.add(self.tvClassMembers)
  2994.         else:
  2995.             self.changed.discard(self.tvClassMembers)
  2996.         self.setDataButtonState()
  2997.  
  2998.     def on_btnClassDelMember_clicked(self, button):
  2999.         moveClassMembers(self.tvClassMembers,
  3000.                          self.tvClassNotMembers)
  3001.         if getCurrentClassMembers(self.tvClassMembers) != self.printer.class_members:
  3002.             self.changed.add(self.tvClassMembers)
  3003.         else:
  3004.             self.changed.discard(self.tvClassMembers)
  3005.         self.setDataButtonState()
  3006.  
  3007.     # Quit
  3008.  
  3009.     def on_quit_activate(self, widget, event=None):
  3010.         self.monitor.cleanup ()
  3011.         while len (self.jobviewers) > 0:
  3012.             # this will call on_jobviewer_exit
  3013.             self.jobviewers[0].on_delete_event ()
  3014.         del self.mainlist
  3015.         del self.printers
  3016.         gtk.main_quit()
  3017.  
  3018.     # Rename
  3019.     def is_rename_possible (self, name):
  3020.         jobs = self.printers[name].jobsQueued (limit=1)
  3021.         if len (jobs) > 0:
  3022.             show_error_dialog (_("Cannot Rename"),
  3023.                                _("There are queued jobs."),
  3024.                                parent=self.PrintersWindow)
  3025.             return False
  3026.  
  3027.         return True
  3028.  
  3029.     def rename_confirmed_by_user (self, name):
  3030.         """
  3031.         Renaming deletes job history. So if we have some completed jobs,
  3032.         inform the user and let him confirm the renaming.
  3033.         """
  3034.         preserved_jobs = self.printers[name].jobsPreserved(limit=1)
  3035.         if len (preserved_jobs) > 0:
  3036.             dialog = gtk.MessageDialog (self.PrintersWindow,
  3037.                                         gtk.DIALOG_MODAL |
  3038.                                         gtk.DIALOG_DESTROY_WITH_PARENT,
  3039.                                         gtk.MESSAGE_WARNING,
  3040.                                         gtk.BUTTONS_OK_CANCEL,
  3041.                                         _("Renaming will lose history"))
  3042.  
  3043.             dialog.format_secondary_text (_("Completed jobs will no longer "
  3044.                                             "be available for re-printing."))
  3045.             result = dialog.run()
  3046.             dialog.destroy ()
  3047.             if result == gtk.RESPONSE_CANCEL:
  3048.                 return False
  3049.  
  3050.         return True
  3051.  
  3052.     def on_rename_activate(self, UNUSED):
  3053.         tuple = self.dests_iconview.get_cursor ()
  3054.         if tuple == None:
  3055.             return
  3056.  
  3057.         (path, cell) = tuple
  3058.         if type (cell) != gtk.CellRendererText:
  3059.             cells = self.dests_iconview.get_cells ()
  3060.             for cell in cells:
  3061.                 if type (cell) == gtk.CellRendererText:
  3062.                     break
  3063.             if type (cell) != gtk.CellRendererText:
  3064.                 return
  3065.  
  3066.         model = self.dests_iconview.get_model ()
  3067.         iter = model.get_iter (path)
  3068.         name = unicode (model.get_value (iter, 2))
  3069.         if not self.is_rename_possible (name):
  3070.             return
  3071.         if not self.rename_confirmed_by_user (name):
  3072.             return
  3073.         cell.set_property ('editable', True)
  3074.         self.dests_iconview.set_cursor (path, cell, start_editing=True)
  3075.         ids = []
  3076.         ids.append (cell.connect ('edited', self.printer_name_edited))
  3077.         ids.append (cell.connect ('editing-canceled',
  3078.                                  self.printer_name_edit_cancel))
  3079.         self.rename_sigids = ids
  3080.  
  3081.     def printer_name_edited (self, cell, path, newname):
  3082.         model = self.dests_iconview.get_model ()
  3083.         iter = model.get_iter (path)
  3084.         name = unicode (model.get_value (iter, 2))
  3085.         debugprint ("edited: %s -> %s" % (name, newname))
  3086.         try:
  3087.             self.rename_printer (name, newname)
  3088.         finally:
  3089.             cell.stop_editing (canceled=False)
  3090.             cell.set_property ('editable', False)
  3091.             for id in self.rename_sigids:
  3092.                 cell.disconnect (id)
  3093.  
  3094.     def printer_name_edit_cancel (self, cell):
  3095.         debugprint ("editing-canceled")
  3096.         cell.stop_editing (canceled=True)
  3097.         cell.set_property ('editable', False)
  3098.         for id in self.rename_sigids:
  3099.             cell.disconnect (id)
  3100.  
  3101.     def rename_printer (self, old_name, new_name):
  3102.         if old_name == new_name:
  3103.             return
  3104.  
  3105.         try:
  3106.             self.fillPrinterTab (old_name)
  3107.         except RuntimeError:
  3108.             # Perhaps cupsGetPPD2 failed for a browsed printer
  3109.             pass
  3110.         except cups.IPPError, (e, m):
  3111.             show_IPP_Error (e, m, self.PrintersWindow)
  3112.             self.populateList ()
  3113.             return
  3114.  
  3115.         if not self.is_rename_possible (old_name):
  3116.             return
  3117.  
  3118.         self.cups._begin_operation (_("renaming printer"))
  3119.         rejecting = self.printer.rejecting
  3120.         if not rejecting:
  3121.             try:
  3122.                 self.printer.setAccepting (False)
  3123.                 if not self.is_rename_possible (old_name):
  3124.                     self.printer.setAccepting (True)
  3125.                     self.cups._end_operation ()
  3126.                     return
  3127.             except cups.IPPError, (e, msg):
  3128.                 show_IPP_Error (e, msg, self.PrintersWindow)
  3129.                 self.cups._end_operation ()
  3130.                 return
  3131.  
  3132.         if self.duplicate_printer (new_name):
  3133.             # Failure.
  3134.             self.monitor.update ()
  3135.  
  3136.             # Restore original accepting/rejecting state.
  3137.             if not rejecting:
  3138.                 try:
  3139.                     self.printer.name = old_name
  3140.                     self.printer.setAccepting (True)
  3141.                 except cups.HTTPError, (s,):
  3142.                     show_HTTP_Error (s, self.PrintersWindow)
  3143.                 except cups.IPPError, (e, msg):
  3144.                     show_IPP_Error (e, msg, self.PrintersWindow)
  3145.  
  3146.             self.cups._end_operation ()
  3147.             self.populateList ()
  3148.             return
  3149.  
  3150.         # Restore rejecting state.
  3151.         if not rejecting:
  3152.             try:
  3153.                 self.printer.setAccepting (True)
  3154.             except cups.HTTPError, (s,):
  3155.                 show_HTTP_Error (s, self.PrintersWindow)
  3156.                 # Not fatal.
  3157.             except cups.IPPError, (e, msg):
  3158.                 show_IPP_Error (e, msg, self.PrintersWindow)
  3159.                 # Not fatal.
  3160.  
  3161.         # Fix up default printer.
  3162.         if self.default_printer == old_name:
  3163.             reload = False
  3164.             try:
  3165.                 reload = self.printer.setAsDefault ()
  3166.             except cups.HTTPError, (s,):
  3167.                 show_HTTP_Error (s, self.PrintersWindow)
  3168.                 # Not fatal.
  3169.             except cups.IPPError, (e, msg):
  3170.                 show_IPP_Error (e, msg, self.PrintersWindow)
  3171.                 # Not fatal.
  3172.  
  3173.             if reload:
  3174.                 self.reconnect ()
  3175.  
  3176.         # Finally, delete the old printer.
  3177.         try:
  3178.             self.cups.deletePrinter (old_name)
  3179.         except cups.HTTPError, (s,):
  3180.             show_HTTP_Error (s, self.PrintersWindow)
  3181.             # Not fatal
  3182.         except cups.IPPError, (e, msg):
  3183.             show_IPP_Error (e, msg, self.PrintersWindow)
  3184.             # Not fatal.
  3185.  
  3186.         self.cups._end_operation ()
  3187.  
  3188.         # ..and select the new printer.
  3189.         def select_new_printer (model, path, iter):
  3190.             name = unicode (model.get_value (iter, 2))
  3191.             print name, new_name
  3192.             if name == new_name:
  3193.                 self.dests_iconview.select_path (path)
  3194.         self.populateList ()
  3195.         model = self.dests_iconview.get_model ()
  3196.         model.foreach (select_new_printer)
  3197.  
  3198.     # Duplicate
  3199.  
  3200.     def duplicate_printer (self, new_name):
  3201.         self.printer.name = new_name
  3202.         self.printer.class_members = [] # for classes make sure all members
  3203.                                         # will get added
  3204.  
  3205.         self.cups._begin_operation (_("duplicating printer"))
  3206.         ret = self.save_printer(self.printer, saveall=True,
  3207.                                 parent=self.PrintersWindow)
  3208.         self.cups._end_operation ()
  3209.         return ret
  3210.  
  3211.     def on_duplicate_activate(self, UNUSED):
  3212.         iconview = self.dests_iconview
  3213.         paths = iconview.get_selected_items ()
  3214.         model = self.dests_iconview.get_model ()
  3215.         iter = model.get_iter (paths[0])
  3216.         name = unicode (model.get_value (iter, 2))
  3217.         self.entDuplicateName.set_text(name)
  3218.         self.NewPrinterName.set_transient_for (self.PrintersWindow)
  3219.         result = self.NewPrinterName.run()
  3220.         self.NewPrinterName.hide()
  3221.  
  3222.         if result == gtk.RESPONSE_CANCEL:
  3223.             return
  3224.  
  3225.         try:
  3226.             self.fillPrinterTab (name)
  3227.         except RuntimeError:
  3228.             # Perhaps cupsGetPPD2 failed for a browsed printer
  3229.             pass
  3230.  
  3231.         self.duplicate_printer (self.entDuplicateName.get_text ())
  3232.         self.monitor.update ()
  3233.  
  3234.     def on_entDuplicateName_changed(self, widget):
  3235.         # restrict
  3236.         text = unicode (widget.get_text())
  3237.         new_text = text
  3238.         new_text = new_text.replace("/", "")
  3239.         new_text = new_text.replace("#", "")
  3240.         new_text = new_text.replace(" ", "")
  3241.         if text!=new_text:
  3242.             widget.set_text(new_text)
  3243.         self.btnDuplicateOk.set_sensitive(
  3244.             self.checkNPName(new_text))
  3245.  
  3246.     # Delete
  3247.  
  3248.     def on_delete_activate(self, UNUSED):
  3249.         if isinstance (self.current_groups_pane_item, StaticGroupItem):
  3250.             paths = self.dests_iconview.get_selected_items ()
  3251.             model = self.dests_iconview.get_model ()
  3252.             selected_names = []
  3253.             for path in paths:
  3254.                 selected_names.append (model[path][2])
  3255.             self.current_groups_pane_item.remove_queues (selected_names)
  3256.             self.populateList ()
  3257.         else:
  3258.             self.delete_selected_printer_queues ()
  3259.  
  3260.     def delete_selected_printer_queues (self):
  3261.         paths = self.dests_iconview.get_selected_items ()
  3262.         model = self.dests_iconview.get_model ()
  3263.         n = len (paths)
  3264.         if n == 1:
  3265.             iter = model.get_iter (paths[0])
  3266.             object = model.get_value (iter, 0)
  3267.             name = model.get_value (iter, 2)
  3268.             if object.is_class:
  3269.                 message_format = _("Really delete class '%s'?") % name
  3270.             else:
  3271.                 message_format = _("Really delete printer '%s'?") % name
  3272.         else:
  3273.             message_format = _("Really delete selected destinations?")
  3274.  
  3275.         dialog = gtk.MessageDialog(self.PrintersWindow,
  3276.                                    gtk.DIALOG_DESTROY_WITH_PARENT |
  3277.                                    gtk.DIALOG_MODAL,
  3278.                                    gtk.MESSAGE_WARNING,
  3279.                                    gtk.BUTTONS_NONE,
  3280.                                    message_format)
  3281.         dialog.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
  3282.                             gtk.STOCK_DELETE, gtk.RESPONSE_ACCEPT)
  3283.         dialog.set_default_response (gtk.RESPONSE_REJECT)
  3284.         result = dialog.run()
  3285.         dialog.destroy()
  3286.  
  3287.         if result != gtk.RESPONSE_ACCEPT:
  3288.             return
  3289.  
  3290.         try:
  3291.             for path in paths:
  3292.                 iter = model.get_iter (path)
  3293.                 name = model.get_value (iter, 2)
  3294.                 self.cups._begin_operation (_("deleting printer %s") % name)
  3295.                 name = unicode (name)
  3296.                 self.cups.deletePrinter (name)
  3297.                 self.cups._end_operation ()
  3298.         except cups.IPPError, (e, msg):
  3299.             self.cups._end_operation ()
  3300.             show_IPP_Error(e, msg, self.PrintersWindow)
  3301.  
  3302.         self.changed = set()
  3303.         self.monitor.update ()
  3304.  
  3305.     # Enable/disable
  3306.     def on_enabled_activate(self, toggle_action):
  3307.         if self.updating_widgets:
  3308.             return
  3309.         enable = toggle_action.get_active ()
  3310.         iconview = self.dests_iconview
  3311.         paths = iconview.get_selected_items ()
  3312.         model = iconview.get_model ()
  3313.         for path in paths:
  3314.             iter = model.get_iter (path)
  3315.             printer = model.get_value (iter, 0)
  3316.             name = unicode (model.get_value (iter, 2), 'utf-8')
  3317.             self.cups._begin_operation (_("modifying printer %s") % name)
  3318.             try:
  3319.                 printer.setEnabled (enable)
  3320.             except cups.IPPError, (e, m):
  3321.                 errordialogs.show_IPP_Error (e, m, self.PrintersWindow)
  3322.                 # Give up on this operation.
  3323.                 self.cups._end_operation ()
  3324.                 break
  3325.  
  3326.             self.cups._end_operation ()
  3327.  
  3328.         self.monitor.update ()
  3329.  
  3330.     # Shared
  3331.     def on_shared_activate(self, menuitem):
  3332.         if self.updating_widgets:
  3333.             return
  3334.         share = menuitem.get_active ()
  3335.         iconview = self.dests_iconview
  3336.         paths = iconview.get_selected_items ()
  3337.         model = iconview.get_model ()
  3338.         success = False
  3339.         for path in paths:
  3340.             iter = model.get_iter (path)
  3341.             printer = model.get_value (iter, 0)
  3342.             self.cups._begin_operation (_("modifying printer %s") %
  3343.                                         printer.name)
  3344.             try:
  3345.                 printer.setShared (share)
  3346.                 success = True
  3347.             except cups.IPPError, (e, m):
  3348.                 show_IPP_Error(e, m, self.PrintersWindow)
  3349.                 self.cups._end_operation ()
  3350.                 # Give up on this operation.
  3351.                 break
  3352.  
  3353.             self.cups._end_operation ()
  3354.  
  3355.         if success and share:
  3356.             if self.server_is_publishing == None:
  3357.                 # We haven't yet seen a server-is-sharing-printers attribute.
  3358.                 # Assuming CUPS 1.4, this means we haven't opened a
  3359.                 # properties dialog yet.  Fetch the attributes now and
  3360.                 # look for it.
  3361.                 try:
  3362.                     printer.getAttributes ()
  3363.                     p = printer.other_attributes['server-is-sharing-printers']
  3364.                     self.server_is_publishing = p
  3365.                 except (cups.IPPError, KeyError):
  3366.                     pass
  3367.  
  3368.             self.advise_publish ()
  3369.  
  3370.         # For some reason CUPS doesn't give us a notification about
  3371.         # printers changing 'shared' state, so refresh instead of
  3372.         # update.  We have to defer this to prevent signal problems.
  3373.         def deferred_refresh ():
  3374.             gtk.gdk.threads_enter ()
  3375.             self.populateList ()
  3376.             gtk.gdk.threads_leave ()
  3377.             return False
  3378.         gobject.idle_add (deferred_refresh)
  3379.  
  3380.     def advise_publish(self):
  3381.         if not self.server_is_publishing:
  3382.             show_info_dialog (_("Publish Shared Printers"),
  3383.                               _("Shared printers are not available "
  3384.                                 "to other people unless the "
  3385.                                 "'Publish shared printers' option is "
  3386.                                 "enabled in the server settings."),
  3387.                               parent=self.PrintersWindow)
  3388.  
  3389.     # Set As Default
  3390.     def on_set_as_default_activate(self, UNUSED):
  3391.         iconview = self.dests_iconview
  3392.         paths = iconview.get_selected_items ()
  3393.         model = iconview.get_model ()
  3394.         iter = model.get_iter (paths[0])
  3395.         name = unicode (model.get_value (iter, 2))
  3396.         self.set_system_or_user_default_printer (name)
  3397.  
  3398.     def on_edit_activate (self, UNUSED):
  3399.         paths = self.dests_iconview.get_selected_items ()
  3400.         self.dests_iconview_item_activated (self.dests_iconview, paths[0])
  3401.  
  3402.     def on_create_class_activate (self, UNUSED):
  3403.         paths = self.dests_iconview.get_selected_items ()
  3404.         class_members = []
  3405.         model = self.dests_iconview.get_model ()
  3406.         for path in paths:
  3407.             iter = model.get_iter (path)
  3408.             name = unicode (model.get_value (iter, 2), 'utf-8')
  3409.             class_members.append (name)
  3410.         self.newPrinterGUI.init ("class")
  3411.         out_model = self.newPrinterGUI.tvNCNotMembers.get_model ()
  3412.         in_model = self.newPrinterGUI.tvNCMembers.get_model ()
  3413.         iter = out_model.get_iter_first ()
  3414.         while iter != None:
  3415.             next = out_model.iter_next (iter)
  3416.             data = out_model.get (iter, 0)
  3417.             if data[0] in class_members:
  3418.                 in_model.append (data)
  3419.                 out_model.remove (iter)
  3420.             iter = next
  3421.  
  3422.     def on_view_print_queue_activate (self, UNUSED):
  3423.         paths = self.dests_iconview.get_selected_items ()
  3424.         if len (paths):
  3425.             specific_dests = []
  3426.             model = self.dests_iconview.get_model ()
  3427.             for path in paths:
  3428.                 iter = model.get_iter (path)
  3429.                 name = unicode (model.get_value (iter, 2), 'utf-8')
  3430.                 specific_dests.append (name)
  3431.             viewer = jobviewer.JobViewer (None, None, my_jobs=False,
  3432.                                           specific_dests=specific_dests,
  3433.                                           exit_handler=self.on_jobviewer_exit,
  3434.                                           parent=self.PrintersWindow)
  3435.         else:
  3436.             viewer = jobviewer.JobViewer (None, None, my_jobs=False,
  3437.                                           exit_handler=self.on_jobviewer_exit,
  3438.                                           parent=self.PrintersWindow)
  3439.  
  3440.         self.jobviewers.append (viewer)
  3441.  
  3442.     def on_jobviewer_exit (self, viewer):
  3443.         i = self.jobviewers.index (viewer)
  3444.         del self.jobviewers[i]
  3445.  
  3446.     def on_view_groups_activate (self, widget):
  3447.         if widget.get_active ():
  3448.             if not self.groups_pane_visible:
  3449.                 # Show it.
  3450.                 self.view_area_vbox.remove (self.view_area_scrolledwindow)
  3451.                 self.view_area_hpaned.add2 (self.view_area_scrolledwindow)
  3452.                 self.view_area_vbox.add (self.view_area_hpaned)
  3453.                 self.view_area_vbox.show_all ()
  3454.                 self.groups_pane_visible = True
  3455.         else:
  3456.             if self.groups_pane_visible:
  3457.                 # Hide it.
  3458.                 self.view_area_vbox.remove (self.view_area_hpaned)
  3459.                 self.view_area_hpaned.remove (self.view_area_scrolledwindow)
  3460.                 self.view_area_vbox.add (self.view_area_scrolledwindow)
  3461.                 self.view_area_vbox.show_all ()
  3462.                 self.groups_pane_visible = False
  3463.  
  3464.     def on_view_discovered_printers_activate (self, UNUSED):
  3465.         self.populateList ()
  3466.  
  3467.     def on_troubleshoot_activate(self, widget):
  3468.         if not self.__dict__.has_key ('troubleshooter'):
  3469.             self.troubleshooter = troubleshoot.run (self.on_troubleshoot_quit)
  3470.  
  3471.     def on_troubleshoot_quit(self, troubleshooter):
  3472.         del self.troubleshooter
  3473.  
  3474.     def on_save_as_group_activate (self, UNUSED):
  3475.         model = self.dests_iconview.get_model ()
  3476.         printer_queues = []
  3477.         for object in model:
  3478.             printer_queues.append (object[2])
  3479.         self.groups_pane.create_new_group (printer_queues,
  3480.                                            self.current_filter_text)
  3481.  
  3482.     def on_save_as_search_group_activate (self, UNUSED):
  3483.         criterion = None
  3484.         if self.current_filter_mode == "filter-name":
  3485.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_NAME,
  3486.                                          value   = self.current_filter_text)
  3487.         elif self.current_filter_mode == "filter-description":
  3488.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_DESC,
  3489.                                          value   = self.current_filter_text)
  3490.         elif self.current_filter_mode == "filter-location":
  3491.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_LOCATION,
  3492.                                          value   = self.current_filter_text)
  3493.         elif self.current_filter_mode == "filter-manufacturer":
  3494.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_MANUF,
  3495.                                          value   = self.current_filter_text)
  3496.         else:
  3497.             nonfatalException ()
  3498.             return
  3499.  
  3500.         self.groups_pane.create_new_search_group (criterion,
  3501.                                                   self.current_filter_text)
  3502.  
  3503.     # About dialog
  3504.     def on_about_activate(self, widget):
  3505.         self.AboutDialog.set_transient_for (self.PrintersWindow)
  3506.         self.AboutDialog.run()
  3507.         self.AboutDialog.hide()
  3508.  
  3509.     ##########################################################################
  3510.     ### Server settings
  3511.     ##########################################################################
  3512.  
  3513.     def fillServerTab(self):
  3514.         self.changed = set()
  3515.         self.cups._begin_operation (_("fetching server settings"))
  3516.         try:
  3517.             self.server_settings = self.cups.adminGetServerSettings()
  3518.         except cups.IPPError, (e, m):
  3519.             show_IPP_Error(e, m, self.PrintersWindow)
  3520.             self.cups._end_operation ()
  3521.             raise
  3522.  
  3523.         self.cups._end_operation ()
  3524.  
  3525.         for widget, setting in [
  3526.             (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS),
  3527.             (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS),
  3528.             (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY),
  3529.             (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN),
  3530.             (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY),
  3531.             (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING),]:
  3532.             widget.set_data("setting", setting)
  3533.             if self.server_settings.has_key(setting):
  3534.                 widget.set_active(int(self.server_settings[setting]))
  3535.                 widget.set_sensitive(True)
  3536.             else:
  3537.                 widget.set_active(False)
  3538.                 widget.set_sensitive(False)
  3539.  
  3540.         try:
  3541.             flag = cups.CUPS_SERVER_SHARE_PRINTERS
  3542.             publishing = int (self.server_settings[flag])
  3543.             self.server_is_publishing = publishing
  3544.         except AttributeError:
  3545.             pass
  3546.  
  3547.         # Set sensitivity of 'Allow printing from the Internet'.
  3548.         self.on_server_changed (self.chkServerShare) # (any will do here)
  3549.  
  3550.     def on_server_changed(self, widget):
  3551.         setting = widget.get_data("setting")
  3552.         if self.server_settings.has_key (setting):
  3553.             if str(int(widget.get_active())) == self.server_settings[setting]:
  3554.                 self.changed.discard(widget)
  3555.             else:
  3556.                 self.changed.add(widget)
  3557.  
  3558.         sharing = self.chkServerShare.get_active ()
  3559.         self.chkServerShareAny.set_sensitive (
  3560.             sharing and self.server_settings.has_key(try_CUPS_SERVER_REMOTE_ANY))
  3561.  
  3562.     def save_serversettings(self):
  3563.         setting_dict = dict()
  3564.         for widget, setting in [
  3565.             (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS),
  3566.             (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS),
  3567.             (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY),
  3568.             (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN),
  3569.             (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY),
  3570.             (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING),]:
  3571.             if not self.server_settings.has_key(setting): continue
  3572.             setting_dict[setting] = str(int(widget.get_active()))
  3573.         self.cups._begin_operation (_("modifying server settings"))
  3574.         try:
  3575.             self.cups.adminSetServerSettings(setting_dict)
  3576.         except cups.IPPError, (e, m):
  3577.             show_IPP_Error(e, m, self.ServerSettingsDialog)
  3578.             self.cups._end_operation ()
  3579.             return True
  3580.         except RuntimeError, s:
  3581.             show_IPP_Error(None, s, self.ServerSettingsDialog)
  3582.             self.cups._end_operation ()
  3583.             return True
  3584.         self.cups._end_operation ()
  3585.         self.changed = set()
  3586.  
  3587.         old_setting = self.server_settings.get (cups.CUPS_SERVER_SHARE_PRINTERS,
  3588.                                                 '0')
  3589.         new_setting = setting_dict.get (cups.CUPS_SERVER_SHARE_PRINTERS, '0')
  3590.         if (old_setting == '0' and new_setting != '0'):
  3591.             # We have just enabled print queue sharing.
  3592.             # Let's see if the firewall will allow IPP TCP packets in.
  3593.             try:
  3594.                 if (self.connect_server == 'localhost' or
  3595.                     self.connect_server[0] == '/'):
  3596.                     f = firewall.Firewall ()
  3597.                     allowed = f.check_ipp_server_allowed ()
  3598.                 else:
  3599.                     # This is a remote server.  Nothing we can do
  3600.                     # about the firewall there.
  3601.                     allowed = True
  3602.  
  3603.                 if not allowed:
  3604.                     dialog = gtk.MessageDialog (self.ServerSettingsDialog,
  3605.                                                 gtk.DIALOG_MODAL |
  3606.                                                 gtk.DIALOG_DESTROY_WITH_PARENT,
  3607.                                                 gtk.MESSAGE_QUESTION,
  3608.                                                 gtk.BUTTONS_NONE,
  3609.                                                 _("Adjust Firewall"))
  3610.                     dialog.format_secondary_text (_("Adjust the firewall now "
  3611.                                                     "to allow all incoming IPP "
  3612.                                                     "connections?"))
  3613.                     dialog.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
  3614.                                         _("Adjust Firewall"), gtk.RESPONSE_YES)
  3615.                     response = dialog.run ()
  3616.                     dialog.destroy ()
  3617.  
  3618.                     if response == gtk.RESPONSE_YES:
  3619.                         f.add_rule (f.ALLOW_IPP_SERVER)
  3620.                         f.write ()
  3621.             except (dbus.DBusException, Exception):
  3622.                 nonfatalException ()
  3623.  
  3624.         time.sleep(1) # give the server a chance to process our request
  3625.  
  3626.         # Now reconnect, in case the server needed to reload.
  3627.         self.reconnect ()
  3628.  
  3629.         # Refresh the server settings in case they have changed in the
  3630.         # mean time.
  3631.         try:
  3632.             self.fillServerTab()
  3633.         except:
  3634.             nonfatalException()
  3635.  
  3636.     ### The "Problems?" clickable label
  3637.     def on_problems_button_clicked (self, *args):
  3638.         if not self.__dict__.has_key ('troubleshooter'):
  3639.             self.troubleshooter = troubleshoot.run (self.on_troubleshoot_quit,
  3640.                                                     parent=self.ServerSettingsDialog)
  3641.  
  3642.     # ====================================================================
  3643.     # == New Printer Dialog ==============================================
  3644.     # ====================================================================
  3645.  
  3646.     # new printer
  3647.     def on_new_printer_activate(self, widget):
  3648.         self.busy (self.PrintersWindow)
  3649.         self.newPrinterGUI.init("printer")
  3650.         self.ready (self.PrintersWindow)
  3651.  
  3652.     # new printer, auto-detected, but now driver found
  3653.     def on_autodetected_printer_without_driver(self, widget):
  3654.         self.busy (self.PrintersWindow)
  3655.         self.newPrinterGUI.init("printer_with_uri")
  3656.         self.ready (self.PrintersWindow)
  3657.  
  3658.     # new class
  3659.     def on_new_class_activate(self, widget):
  3660.         self.newPrinterGUI.init("class")
  3661.  
  3662.     # change device
  3663.     def on_btnSelectDevice_clicked(self, button):
  3664.         self.busy (self.PrintersWindow)
  3665.         self.newPrinterGUI.init("device")
  3666.         self.ready (self.PrintersWindow)
  3667.  
  3668.     # change PPD
  3669.     def on_btnChangePPD_clicked(self, button):
  3670.         self.busy (self.PrintersWindow)
  3671.         self.newPrinterGUI.init("ppd")
  3672.         self.ready (self.PrintersWindow)
  3673.  
  3674.     def checkNPName(self, name):
  3675.         if not name: return False
  3676.         name = unicode (name.lower())
  3677.         for printer in self.printers.values():
  3678.             if not printer.discovered and printer.name.lower()==name:
  3679.                 return False
  3680.         return True
  3681.  
  3682.     def makeNameUnique(self, name):
  3683.         """Make a suggested queue name valid and unique."""
  3684.         name = name.replace (" ", "-")
  3685.         name = name.replace ("/", "-")
  3686.         name = name.replace ("#", "-")
  3687.         if not self.checkNPName (name):
  3688.             suffix=2
  3689.             while not self.checkNPName (name + "-" + str (suffix)):
  3690.                 suffix += 1
  3691.                 if suffix == 100:
  3692.                     break
  3693.             name += "-" + str (suffix)
  3694.         return name
  3695.  
  3696.     ## Watcher interface helpers
  3697.     def printer_added_or_removed (self):
  3698.         # Just fetch the list of printers again.  This is too simplistic.
  3699.         gtk.gdk.threads_enter ()
  3700.         self.populateList (prompt_allowed=False)
  3701.         gtk.gdk.threads_leave ()
  3702.  
  3703.     ## Watcher interface
  3704.     def printer_added (self, mon, printer):
  3705.         monitor.Watcher.printer_added (self, mon, printer)
  3706.         self.printer_added_or_removed ()
  3707.  
  3708.     def printer_event (self, mon, printer, eventname, event):
  3709.         monitor.Watcher.printer_event (self, mon, printer, eventname, event)
  3710.  
  3711.         def deferred_refresh ():
  3712.             gtk.gdk.threads_enter ()
  3713.             self.populateList ()
  3714.             gtk.gdk.threads_leave ()
  3715.             return False
  3716.  
  3717.         gtk.gdk.threads_enter ()
  3718.         if self.printers.has_key (printer):
  3719.             self.printers[printer].update (**event)
  3720.             self.dests_iconview_selection_changed (self.dests_iconview)
  3721.             gobject.idle_add (deferred_refresh)
  3722.             if self.PrinterPropertiesDialog.get_property('visible'):
  3723.                 try:
  3724.                     self.printer.getAttributes ()
  3725.                     self.updatePrinterProperties ()
  3726.                 except cups.IPPError:
  3727.                     pass
  3728.  
  3729.         gtk.gdk.threads_leave ()
  3730.  
  3731.     def printer_removed (self, mon, printer):
  3732.         monitor.Watcher.printer_removed (self, mon, printer)
  3733.         self.printer_added_or_removed ()
  3734.  
  3735.     def state_reason_added (self, mon, reason):
  3736.         monitor.Watcher.state_reason_added (self, mon, reason)
  3737.         gtk.gdk.threads_enter ()
  3738.         if self.PrinterPropertiesDialog.get_property('visible'):
  3739.             try:
  3740.                 self.printer.getAttributes ()
  3741.                 self.updatePrinterProperties ()
  3742.             except cups.IPPError:
  3743.                 pass
  3744.  
  3745.         gtk.gdk.threads_leave ()
  3746.  
  3747.     def state_reason_removed (self, mon, reason):
  3748.         monitor.Watcher.state_reason_removed (self, mon, reason)
  3749.         gtk.gdk.threads_enter ()
  3750.         if self.PrinterPropertiesDialog.get_property('visible'):
  3751.             try:
  3752.                 self.printer.getAttributes ()
  3753.                 self.updatePrinterProperties ()
  3754.             except cups.IPPError:
  3755.                 pass
  3756.  
  3757.         gtk.gdk.threads_leave ()
  3758.  
  3759.     def cups_connection_error (self, mon):
  3760.         monitor.Watcher.cups_connection_error (self, mon)
  3761.         try:
  3762.             self.cups.getClasses ()
  3763.         except:
  3764.             self.cups = None
  3765.             gtk.gdk.threads_enter ()
  3766.             self.setConnected ()
  3767.             self.populateList (prompt_allowed=False)
  3768.             gtk.gdk.threads_leave ()
  3769.  
  3770. class NewPrinterGUI(GtkGUI):
  3771.  
  3772.     new_printer_device_tabs = {
  3773.         "parallel" : 0, # empty tab
  3774.         "usb" : 0,
  3775.         "hal" : 0,
  3776.         "beh" : 0,
  3777.         "hp" : 0,
  3778.         "hpfax" : 0,
  3779.         "socket": 2,
  3780.         "ipp" : 3,
  3781.         "http" : 3,
  3782.         "https" : 3,
  3783.         "lpd" : 4,
  3784.         "scsi" : 5,
  3785.         "serial" : 6,
  3786.         "smb" : 7,
  3787.         "network": 8,
  3788.         }
  3789.  
  3790.     DOWNLOADABLE_ONLYPPD=True
  3791.  
  3792.     def __init__(self, mainapp):
  3793.         self.mainapp = mainapp
  3794.         self.language = mainapp.language
  3795.  
  3796.         self.options = {} # keyword -> Option object
  3797.         self.changed = set()
  3798.         self.conflicts = set()
  3799.         self.device = None
  3800.         self.ppd = None
  3801.         self.remotecupsqueue = False
  3802.         self.exactdrivermatch = False
  3803.         self.installable_options = False
  3804.         self.ppdsloader = None
  3805.         self.jockey_installed_files = []
  3806.  
  3807.         # Synchronisation objects.
  3808.         self.drivers_lock = thread.allocate_lock()
  3809.  
  3810.         self.getWidgets({"NewPrinterWindow":
  3811.                              ["NewPrinterWindow",
  3812.                               "ntbkNewPrinter",
  3813.                               "btnNPBack",
  3814.                               "btnNPForward",
  3815.                               "btnNPApply",
  3816.                               "imgProcessWorking",
  3817.                               "entNPName",
  3818.                               "entNPDescription",
  3819.                               "entNPLocation",
  3820.                               "tvNPDevices",
  3821.                               "ntbkNPType",
  3822.                               "lblNPDeviceDescription",
  3823.                               "expNPDeviceURIs",
  3824.                               "tvNPDeviceURIs",
  3825.                               "cmbNPTSerialBaud",
  3826.                               "cmbNPTSerialParity",
  3827.                               "cmbNPTSerialBits",
  3828.                               "cmbNPTSerialFlow",
  3829.                               "btnNPTLpdProbe",
  3830.                               "cmbentNPTLpdHost",
  3831.                               "cmbentNPTLpdQueue",
  3832.                               "entNPTIPPHostname",
  3833.                               "lblIPPURI",
  3834.                               "entNPTIPPQueuename",
  3835.                               "btnIPPVerify",
  3836.                               "entNPTDirectJetHostname",
  3837.                               "entNPTDirectJetPort",
  3838.                               "entSMBURI",
  3839.                               "btnSMBBrowse",
  3840.                               "tblSMBAuth",
  3841.                               "rbtnSMBAuthPrompt",
  3842.                               "rbtnSMBAuthSet",
  3843.                               "entSMBUsername",
  3844.                               "entSMBPassword",
  3845.                               "btnSMBVerify",
  3846.                               "entNPTNetworkHostname",
  3847.                               "btnNetworkFind",
  3848.                               "lblNetworkFindSearching",
  3849.                               "lblNetworkFindNotFound",
  3850.                               "entNPTDevice",
  3851.                               "tvNCMembers",
  3852.                               "tvNCNotMembers",
  3853.                               "btnNCAddMember",
  3854.                               "btnNCDelMember",
  3855.                               "ntbkPPDSource",
  3856.                               "rbtnNPPPD",
  3857.                               "tvNPMakes",
  3858.                               "rbtnNPFoomatic",
  3859.                               "filechooserPPD",
  3860.                               "rbtnNPDownloadableDriverSearch",
  3861.                               "entNPDownloadableDriverSearch",
  3862.                               "btnNPDownloadableDriverSearch",
  3863.                               "cmbNPDownloadableDriverFoundPrinters",
  3864.                               "tvNPModels",
  3865.                               "tvNPDrivers",
  3866.                               "rbtnChangePPDasIs",
  3867.                               "rbtnChangePPDKeepSettings",
  3868.                               "scrNPInstallableOptions",
  3869.                               "vbNPInstallOptions",
  3870.                               "tvNPDownloadableDrivers",
  3871.                               "ntbkNPDownloadableDriverProperties",
  3872.                               "lblNPDownloadableDriverSupplier",
  3873.                               "cbNPDownloadableDriverSupplierVendor",
  3874.                               "lblNPDownloadableDriverLicense",
  3875.                               "cbNPDownloadableDriverLicensePatents",
  3876.                               "cbNPDownloadableDriverLicenseFree",
  3877.                               "lblNPDownloadableDriverDescription",
  3878.                               "lblNPDownloadableDriverSupportContacts",
  3879.                               "hsDownloadableDriverPerfText",
  3880.                               "hsDownloadableDriverPerfLineArt",
  3881.                               "hsDownloadableDriverPerfGraphics",
  3882.                               "hsDownloadableDriverPerfPhoto",
  3883.                               "lblDownloadableDriverPerfTextUnknown",
  3884.                               "lblDownloadableDriverPerfLineArtUnknown",
  3885.                               "lblDownloadableDriverPerfGraphicsUnknown",
  3886.                               "lblDownloadableDriverPerfPhotoUnknown",
  3887.                               "frmNPDownloadableDriverLicenseTerms",
  3888.                               "tvNPDownloadableDriverLicense",
  3889.                               "rbtnNPDownloadLicenseYes",
  3890.                               "rbtnNPDownloadLicenseNo"],
  3891.                          "WaitWindow":
  3892.                              ["WaitWindow",
  3893.                               "lblWait"],
  3894.                          "SMBBrowseDialog":
  3895.                              ["SMBBrowseDialog",
  3896.                               "tvSMBBrowser",
  3897.                               "btnSMBBrowseOk"],
  3898.                          "InstallDialog":
  3899.                              ["InstallDialog",
  3900.                               "lblInstall"]},
  3901.  
  3902.                         domain=domain)
  3903.  
  3904.         # Fill in liststores for combo-box widgets
  3905.         for (widget,
  3906.              opts) in [(self.cmbNPTSerialBaud,
  3907.                         [[_("Default")],
  3908.                          [_("1200")],
  3909.                          [_("2400")],
  3910.                          [_("4800")],
  3911.                          [_("9600")],
  3912.                          [_("19200")],
  3913.                          [_("38400")],
  3914.                          [_("57600")],
  3915.                          [_("115200")]]),
  3916.  
  3917.                        (self.cmbNPTSerialParity,
  3918.                         [[_("Default")],
  3919.                          [_("None")],
  3920.                          [_("Odd")],
  3921.                          [_("Even")]]),
  3922.  
  3923.                        (self.cmbNPTSerialBits,
  3924.                         [[_("Default")],
  3925.                          [_("8")],
  3926.                          [_("7")]]),
  3927.  
  3928.                        (self.cmbNPTSerialFlow,
  3929.                         [[_("Default")],
  3930.                          [_("None")],
  3931.                          [_("XON/XOFF (Software)")],
  3932.                          [_("RTS/CTS (Hardware)")],
  3933.                          [_("DTR/DSR (Hardware)")]]),
  3934.  
  3935.                        ]:
  3936.             model = gtk.ListStore (gobject.TYPE_STRING)
  3937.             for row in opts:
  3938.                 model.append (row=row)
  3939.  
  3940.             cell = gtk.CellRendererText ()
  3941.             widget.pack_start (cell, True)
  3942.             widget.add_attribute (cell, 'text', 0)
  3943.             widget.set_model (model)
  3944.  
  3945.         # Since some dialogs are reused we can't let the delete-event's
  3946.         # default handler destroy them
  3947.         for dialog in [self.SMBBrowseDialog]:
  3948.             dialog.connect ("delete-event", on_delete_just_hide)
  3949.  
  3950.         # share with mainapp
  3951.         self.busy = mainapp.busy
  3952.         self.ready = mainapp.ready
  3953.  
  3954.         gtk_label_autowrap.set_autowrap(self.NewPrinterWindow)
  3955.  
  3956.         self.ntbkNewPrinter.set_show_tabs(False)
  3957.         self.ntbkPPDSource.set_show_tabs(False)
  3958.         self.ntbkNPType.set_show_tabs(False)
  3959.         self.ntbkNPDownloadableDriverProperties.set_show_tabs(False)
  3960.  
  3961.         self.spinner = gtkspinner.Spinner (self.imgProcessWorking)
  3962.         self.spinner_count = 0
  3963.  
  3964.         # Set up OpenPrinting widgets.
  3965.         self.openprinting = cupshelpers.openprinting.OpenPrinting ()
  3966.         self.openprinting_query_handle = None
  3967.         combobox = self.cmbNPDownloadableDriverFoundPrinters
  3968.         cell = gtk.CellRendererText()
  3969.         combobox.pack_start (cell, True)
  3970.         combobox.add_attribute(cell, 'text', 0)
  3971.         if self.DOWNLOADABLE_ONLYPPD:
  3972.             for widget in [self.cbNPDownloadableDriverLicenseFree,
  3973.                            self.cbNPDownloadableDriverLicensePatents]:
  3974.                 widget.hide ()
  3975.  
  3976.         def protect_toggle (toggle_widget):
  3977.             active = toggle_widget.get_data ('protect_active')
  3978.             if active != None:
  3979.                 toggle_widget.set_active (active)
  3980.  
  3981.         for widget in [self.cbNPDownloadableDriverSupplierVendor,
  3982.                        self.cbNPDownloadableDriverLicenseFree,
  3983.                        self.cbNPDownloadableDriverLicensePatents]:
  3984.             widget.connect ('clicked', protect_toggle)
  3985.  
  3986.         for widget in [self.hsDownloadableDriverPerfText,
  3987.                        self.hsDownloadableDriverPerfLineArt,
  3988.                        self.hsDownloadableDriverPerfGraphics,
  3989.                        self.hsDownloadableDriverPerfPhoto]:
  3990.             widget.connect ('change-value',
  3991.                             lambda x, y, z: True)
  3992.  
  3993.         # Device list
  3994.         slct = self.tvNPDevices.get_selection ()
  3995.         slct.set_select_function (self.device_select_function)
  3996.         self.tvNPDevices.set_row_separator_func (self.device_row_separator_fn)
  3997.         self.tvNPDevices.connect ("row-activated", self.device_row_activated)
  3998.  
  3999.         # Devices expander
  4000.         self.expNPDeviceURIs.connect ("notify::expanded",
  4001.                                       self.on_expNPDeviceURIs_expanded)
  4002.  
  4003.         # SMB browser
  4004.         self.smb_store = gtk.TreeStore (gobject.TYPE_PYOBJECT)
  4005.         self.btnSMBBrowse.set_sensitive (PYSMB_AVAILABLE)
  4006.         if not PYSMB_AVAILABLE:
  4007.             self.btnSMBBrowse.set_tooltip_text (_("Browsing not available "
  4008.                                                   "(pysmbc not installed)"))
  4009.  
  4010.         self.tvSMBBrowser.set_model (self.smb_store)
  4011.  
  4012.         # SMB list columns
  4013.         col = gtk.TreeViewColumn (_("Share"))
  4014.         cell = gtk.CellRendererText ()
  4015.         col.pack_start (cell, False)
  4016.         col.set_cell_data_func (cell, self.smbbrowser_cell_share)
  4017.         self.tvSMBBrowser.append_column (col)
  4018.  
  4019.         col = gtk.TreeViewColumn (_("Comment"))
  4020.         cell = gtk.CellRendererText ()
  4021.         col.pack_start (cell, False)
  4022.         col.set_cell_data_func (cell, self.smbbrowser_cell_comment)
  4023.         self.tvSMBBrowser.append_column (col)
  4024.  
  4025.         slct = self.tvSMBBrowser.get_selection ()
  4026.         slct.set_select_function (self.smb_select_function)
  4027.  
  4028.         self.SMBBrowseDialog.set_transient_for(self.NewPrinterWindow)
  4029.  
  4030.         self.tvNPDrivers.set_has_tooltip(True)
  4031.         self.tvNPDrivers.connect("query-tooltip", self.on_NPDrivers_query_tooltip)
  4032.  
  4033.         ppd_filter = gtk.FileFilter()
  4034.         ppd_filter.set_name(_("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)"))
  4035.         ppd_filter.add_pattern("*.ppd")
  4036.         ppd_filter.add_pattern("*.PPD")
  4037.         ppd_filter.add_pattern("*.ppd.gz")
  4038.         ppd_filter.add_pattern("*.PPD.gz")
  4039.         ppd_filter.add_pattern("*.PPD.GZ")
  4040.         self.filechooserPPD.add_filter(ppd_filter)
  4041.  
  4042.         ppd_filter = gtk.FileFilter()
  4043.         ppd_filter.set_name(_("All files (*)"))
  4044.         ppd_filter.add_pattern("*")
  4045.         self.filechooserPPD.add_filter(ppd_filter)
  4046.  
  4047.     def inc_spinner_task (self):
  4048.         if self.spinner_count == 0:
  4049.             self.imgProcessWorking.show ()
  4050.             self.spinner.start ()
  4051.  
  4052.         self.spinner_count += 1
  4053.  
  4054.     def dec_spinner_task (self):
  4055.         self.spinner_count -= 1
  4056.         if self.spinner_count == 0:
  4057.             self.imgProcessWorking.hide ()
  4058.             self.spinner.stop ()
  4059.  
  4060.     def show_IPP_Error (self, exception, message):
  4061.         return show_IPP_Error (exception, message, parent=self.NewPrinterWindow)
  4062.  
  4063.     def option_changed(self, option):
  4064.         if option.is_changed():
  4065.             self.changed.add(option)
  4066.         else:
  4067.             self.changed.discard(option)
  4068.  
  4069.         if option.conflicts:
  4070.             self.conflicts.add(option)
  4071.         else:
  4072.             self.conflicts.discard(option)
  4073.         self.setDataButtonState()
  4074.  
  4075.         return
  4076.  
  4077.     def setDataButtonState(self):
  4078.         self.btnNPForward.set_sensitive(not bool(self.conflicts))
  4079.  
  4080.     def init(self, dialog_mode):
  4081.         self.dialog_mode = dialog_mode
  4082.         self.options = {} # keyword -> Option object
  4083.         self.changed = set()
  4084.         self.conflicts = set()
  4085.         self.fetchDevices_conn = None
  4086.         self.ppds_result = None
  4087.         self.printer_finder = None
  4088.         self.lblNetworkFindSearching.hide ()
  4089.         self.entNPTNetworkHostname.set_sensitive (True)
  4090.         self.entNPTNetworkHostname.set_text ('')
  4091.         self.btnNetworkFind.set_sensitive (True)
  4092.         self.lblNetworkFindNotFound.hide ()
  4093.  
  4094.         if dialog_mode in ["ppd", "device"]:
  4095.             self.parent = self.mainapp.PrinterPropertiesDialog
  4096.         else:
  4097.             self.parent = self.mainapp.PrintersWindow
  4098.  
  4099.         self.NewPrinterWindow.set_transient_for (self.parent)
  4100.  
  4101.         combobox = self.cmbNPDownloadableDriverFoundPrinters
  4102.         combobox.set_model (gtk.ListStore (str, str))
  4103.         self.entNPDownloadableDriverSearch.set_text ('')
  4104.         button = self.btnNPDownloadableDriverSearch
  4105.         label = button.get_children ()[0].get_children ()[0].get_children ()[1]
  4106.         self.btnNPDownloadableDriverSearch_label = label
  4107.         label.set_text (_("Search"))
  4108.  
  4109.         if self.dialog_mode in ("printer", "printer_with_uri", "class"):
  4110.             if self.dialog_mode == "class":
  4111.                 name_proto = "class"
  4112.             else:
  4113.                 name_proto = "printer"
  4114.             self.entNPName.set_text (self.mainapp.makeNameUnique(name_proto))
  4115.             self.entNPName.grab_focus()
  4116.             for widget in [self.entNPLocation,
  4117.                            self.entNPDescription,
  4118.                            self.entSMBURI, self.entSMBUsername,
  4119.                            self.entSMBPassword]:
  4120.                 widget.set_text('')
  4121.  
  4122.         if self.dialog_mode == "printer_with_uri":
  4123.             device_dict = { }
  4124.             self.device = cupshelpers.Device (self.mainapp.device_uri,
  4125.                                               **device_dict)
  4126.  
  4127.         self.entNPTDirectJetPort.set_text('9100')
  4128.         self.rbtnSMBAuthPrompt.set_active(True)
  4129.  
  4130.         if self.dialog_mode == "printer":
  4131.             self.NewPrinterWindow.set_title(_("New Printer"))
  4132.             # Start on devices page (1, not 0)
  4133.             self.ntbkNewPrinter.set_current_page(1)
  4134.             self.fillDeviceTab()
  4135.             self.rbtnNPFoomatic.set_active (True)
  4136.             self.on_rbtnNPFoomatic_toggled(self.rbtnNPFoomatic)
  4137.  
  4138.         elif self.dialog_mode == "class":
  4139.             self.NewPrinterWindow.set_title(_("New Class"))
  4140.             self.fillNewClassMembers()
  4141.             # Start on name page
  4142.             self.ntbkNewPrinter.set_current_page(0)
  4143.         elif self.dialog_mode == "device":
  4144.             self.NewPrinterWindow.set_title(_("Change Device URI"))
  4145.             self.ntbkNewPrinter.set_current_page(1)
  4146.             self.fillDeviceTab(self.mainapp.printer.device_uri)
  4147.         elif self.dialog_mode == "ppd" or \
  4148.             self.dialog_mode == "printer_with_uri":
  4149.             if self.dialog_mode == "ppd":
  4150.                 self.NewPrinterWindow.set_title(_("Change Driver"))
  4151.             else:
  4152.                 self.NewPrinterWindow.set_title(_("New Printer"))
  4153.  
  4154.             try:
  4155.                 self.fetchPPDs (parent=self.parent)
  4156.             except cups.IPPError, (e, m):
  4157.                 show_IPP_Error (e, m, parent=self.parent)
  4158.                 return
  4159.             except:
  4160.                 nonfatalException ()
  4161.                 return
  4162.  
  4163.             if not self.ppds:
  4164.                 return
  4165.  
  4166.             self.ntbkNewPrinter.set_current_page(2)
  4167.             self.rbtnNPFoomatic.set_active (True)
  4168.             self.on_rbtnNPFoomatic_toggled(self.rbtnNPFoomatic)
  4169.             self.rbtnChangePPDKeepSettings.set_active(True)
  4170.  
  4171.             self.auto_make = ""
  4172.             self.auto_model = ""
  4173.             self.auto_driver = None
  4174.             ppd = self.mainapp.ppd
  4175.             #self.mainapp.devid = "MFG:Samsung;MDL:ML-3560;DES:;CMD:GDI;"
  4176.             devid = self.mainapp.devid
  4177.             if self.dialog_mode == "ppd":
  4178.                 uri = self.mainapp.printer.device_uri
  4179.             else:
  4180.                 uri = self.device.uri
  4181.  
  4182.             self.exactdrivermatch = False
  4183.             if devid != "":
  4184.                 devid_dict = dict()
  4185.                 try:
  4186.                     devid_dict = cupshelpers.parseDeviceID (devid)
  4187.                     (status, ppdname) = self.ppds.\
  4188.                         getPPDNameFromDeviceID (devid_dict["MFG"],
  4189.                                                 devid_dict["MDL"],
  4190.                                                 devid_dict["DES"],
  4191.                                                 devid_dict["CMD"],
  4192.                                                 uri,
  4193.                                                 self.jockey_installed_files)
  4194.  
  4195.                     ppddict = self.ppds.getInfoFromPPDName (ppdname)
  4196.                     make_model = ppddict['ppd-make-and-model']
  4197.                     (self.auto_make, self.auto_model) = \
  4198.                         cupshelpers.ppds.ppdMakeModelSplit (make_model)
  4199.                     if (status == self.ppds.STATUS_SUCCESS and
  4200.                         self.dialog_mode == "printer_with_uri"):
  4201.                             self.exactdrivermatch = True
  4202.                             self.fillMakeList()
  4203.                             self.ntbkNewPrinter.set_current_page(6)
  4204.                             self.nextNPTab(step = 0)
  4205.                 except:
  4206.                     nonfatalException ()
  4207.  
  4208.                 if self.device and not self.device.id:
  4209.                     self.device.id = devid
  4210.                     self.device.id_dict = devid_dict
  4211.  
  4212.                 self.mainapp.devid = ""
  4213.             elif ppd:
  4214.                 attr = ppd.findAttr("NickName")
  4215.                 if not attr:
  4216.                     attr = ppd.findAttr("ModelName")
  4217.  
  4218.                 if attr and attr.value:
  4219.                     mfgmdl = cupshelpers.ppds.ppdMakeModelSplit (attr.value)
  4220.                     (self.auto_make, self.auto_model) = mfgmdl
  4221.  
  4222.                     # Search for ppdname with that make-and-model
  4223.                     ppds = self.ppds.getInfoFromModel (self.auto_make,
  4224.                                                        self.auto_model)
  4225.                     for ppd, info in ppds.iteritems ():
  4226.                         if info.get ("ppd-make-and-model") == attr.value:
  4227.                             self.auto_driver = ppd
  4228.                             break
  4229.             else:
  4230.                 # Special CUPS names for a raw queue.
  4231.                 self.auto_make = 'Generic'
  4232.                 self.auto_model = 'Raw Queue'
  4233.  
  4234.             self.fillMakeList()
  4235.  
  4236.         self.setNPButtons()
  4237.         self.NewPrinterWindow.show()
  4238.  
  4239.     # get PPDs
  4240.  
  4241.     def _getPPDs_reply (self, ppdsloader, exc):
  4242.         if exc:
  4243.             self.ppds_result = exc
  4244.         else:
  4245.             ppds = ppdsloader.get_ppds ()
  4246.             if ppds == None:
  4247.                 self.ppds_result = None
  4248.             else:
  4249.                 language = self.language[0]
  4250.                 self.ppds_result = cupshelpers.ppds.PPDs (ppds,
  4251.                                                           language=language)
  4252.  
  4253.             self.jockey_installed_files = ppdsloader.get_installed_files ()
  4254.  
  4255.         ppdsloader.destroy ()
  4256.         self.ppdsloader = None
  4257.  
  4258.         # Break out of the innermost loop.
  4259.         gtk.main_quit ()
  4260.  
  4261.     def fetchPPDs(self, parent=None):
  4262.         debugprint ("fetchPPDs")
  4263.  
  4264.         # First, let's see if there are drivers to install for this
  4265.         # model.
  4266.         try:
  4267.             devid = self.device.id
  4268.         except:
  4269.             devid = ''
  4270.  
  4271.         if devid == '':
  4272.             try:
  4273.                 devid = self.mainapp.devid
  4274.             except:
  4275.                 pass
  4276.  
  4277.         # Now load the PPDs.
  4278.         if not devid:
  4279.             devid = None
  4280.  
  4281.         host = self.mainapp.connect_server
  4282.         encryption = self.mainapp.connect_encrypt
  4283.         self.ppdsloader = ppdsloader.PPDsLoader (self._getPPDs_reply,
  4284.                                                  device_id=devid,
  4285.                                                  parent=parent,
  4286.                                                  host=host,
  4287.                                                  encryption=encryption)
  4288.  
  4289.         # Wait until we get the reply.
  4290.         gtk.main ()
  4291.  
  4292.         debugprint ("Got PPDs")
  4293.         result = self.ppds_result # atomic operation
  4294.         if isinstance (result, Exception):
  4295.             # Propagate exception.
  4296.             raise result
  4297.  
  4298.         self.ppds = result
  4299.         return result
  4300.  
  4301.     def dropPPDs(self):
  4302.         try:
  4303.             del self.ppds
  4304.         except:
  4305.             pass
  4306.  
  4307.     # Class members
  4308.  
  4309.     def fillNewClassMembers(self):
  4310.         model = self.tvNCMembers.get_model()
  4311.         model.clear()
  4312.         model = self.tvNCNotMembers.get_model()
  4313.         model.clear()
  4314.         for printer in self.mainapp.printers.itervalues():
  4315.             model.append((printer.name,))
  4316.  
  4317.     def on_btnNCAddMember_clicked(self, button):
  4318.         moveClassMembers(self.tvNCNotMembers, self.tvNCMembers)
  4319.         self.btnNPApply.set_sensitive(
  4320.             bool(getCurrentClassMembers(self.tvNCMembers)))
  4321.         button.set_sensitive(False)
  4322.  
  4323.     def on_btnNCDelMember_clicked(self, button):
  4324.         moveClassMembers(self.tvNCMembers, self.tvNCNotMembers)
  4325.         self.btnNPApply.set_sensitive(
  4326.             bool(getCurrentClassMembers(self.tvNCMembers)))
  4327.         button.set_sensitive(False)
  4328.  
  4329.     def on_tvNCMembers_cursor_changed(self, widget):
  4330.         selection = widget.get_selection()
  4331.         model_from, rows = selection.get_selected_rows()
  4332.         self.btnNCDelMember.set_sensitive(rows != [])
  4333.  
  4334.     def on_tvNCNotMembers_cursor_changed(self, widget):
  4335.         selection = widget.get_selection()
  4336.         model_from, rows = selection.get_selected_rows()
  4337.         self.btnNCAddMember.set_sensitive(rows != [])
  4338.  
  4339.     # Navigation buttons
  4340.  
  4341.     def on_NPCancel(self, widget, event=None):
  4342.         if self.fetchDevices_conn:
  4343.             self.fetchDevices_conn.destroy ()
  4344.             self.fetchDevices_conn = None
  4345.             self.dec_spinner_task ()
  4346.  
  4347.         if self.ppdsloader:
  4348.             self.ppdsloader.destroy ()
  4349.             self.ppds_loader = None
  4350.  
  4351.         if self.printer_finder:
  4352.             self.printer_finder.cancel ()
  4353.             self.printer_finder = None
  4354.             self.dec_spinner_task ()
  4355.  
  4356.         self.NewPrinterWindow.hide()
  4357.         if self.openprinting_query_handle != None:
  4358.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  4359.             self.openprinting_query_handle = None
  4360.  
  4361.         self.device = None
  4362.         return True
  4363.  
  4364.     def on_btnNPBack_clicked(self, widget):
  4365.         self.nextNPTab(-1)
  4366.  
  4367.     def on_btnNPForward_clicked(self, widget):
  4368.         self.nextNPTab()
  4369.  
  4370.     def nextNPTab(self, step=1):
  4371.         page_nr = self.ntbkNewPrinter.get_current_page()
  4372.  
  4373.         if self.dialog_mode == "class":
  4374.             order = [0, 4, 5]
  4375.         elif self.dialog_mode == "printer" or \
  4376.                 self.dialog_mode == "printer_with_uri":
  4377.             self.busy (self.NewPrinterWindow)
  4378.             if page_nr == 1: # Device (first page)
  4379.                 self.auto_make, self.auto_model = "", ""
  4380.                 self.auto_driver = None
  4381.                 self.device.uri = self.getDeviceURI()
  4382.  
  4383.                 if (not self.device.id and
  4384.                     self.device.type in ["socket", "lpd", "ipp"]):
  4385.                     # This is a network printer whose model we don't yet know.
  4386.                     # Try to discover it.
  4387.                     self.getNetworkPrinterMakeModel ()
  4388.  
  4389.                 uri = self.device.uri
  4390.                 if uri and uri.startswith ("smb://"):
  4391.                     uri = SMBURI (uri=uri[6:]).sanitize_uri ()
  4392.  
  4393.                 # Try to access the PPD, in this case our detected IPP
  4394.                 # printer is a queue on a remote CUPS server which is
  4395.                 # not automatically set up on our local CUPS server
  4396.                 # (for example DNS-SD broadcasted queue from Mac OS X)
  4397.                 self.remotecupsqueue = None
  4398.                 res = re.search ("ipp://(\S+(:\d+|))/printers/(\S+)", uri)
  4399.                 if res:
  4400.                     resg = res.groups()
  4401.                     try:
  4402.                         conn = httplib.HTTPConnection(resg[0])
  4403.                         conn.request("GET", "/printers/%s.ppd" % resg[2])
  4404.                         resp = conn.getresponse()
  4405.                         if resp.status == 200: self.remotecupsqueue = resg[2]
  4406.                     except:
  4407.                         pass
  4408.  
  4409.                     # We also want to fetch the printer-info and
  4410.                     # printer-location attributes, to pre-fill those
  4411.                     # fields for this new queue.
  4412.                     try:
  4413.                         if len (resg[1]) > 0:
  4414.                             port = int (resg[1])
  4415.                         else:
  4416.                             port = 631
  4417.  
  4418.                         encryption = cups.HTTP_ENCRYPT_IF_REQUESTED
  4419.                         c = cups.Connection (host=resg[0],
  4420.                                              port=port,
  4421.                                              encryption=encryption)
  4422.  
  4423.                         r = ['printer-info', 'printer-location']
  4424.                         attrs = c.getPrinterAttributes (uri=uri,
  4425.                                                         requested_attributes=r)
  4426.                         info = attrs.get ('printer-info', '')
  4427.                         location = attrs.get ('printer-location', '')
  4428.                         if len (info) > 0:
  4429.                             self.entNPDescription.set_text (info)
  4430.                         if len (location) > 0:
  4431.                             self.device.location = location
  4432.                     except RuntimeError:
  4433.                         pass
  4434.                     except:
  4435.                         nonfatalException ()
  4436.  
  4437.                 if not self.remotecupsqueue:
  4438.                     try:
  4439.                         self.fetchPPDs(self.NewPrinterWindow)
  4440.                     except cups.IPPError, (e, msg):
  4441.                         self.ready (self.NewPrinterWindow)
  4442.                         self.show_IPP_Error(e, msg)
  4443.                         return
  4444.                     except:
  4445.                         nonfatalException ()
  4446.                         self.ready (self.NewPrinterWindow)
  4447.                         return
  4448.  
  4449.                     if self.ppds == None:
  4450.                         self.ready (self.NewPrinterWindow)
  4451.                         return
  4452.  
  4453.                 ppdname = None
  4454.                 try:
  4455.                     if self.remotecupsqueue:
  4456.                         # We have a remote CUPS queue, let the client queue
  4457.                         # stay raw so that the driver on the server gets used
  4458.                         ppdname = 'raw'
  4459.                         self.ppd = ppdname
  4460.                         name = self.remotecupsqueue
  4461.                         name = self.mainapp.makeNameUnique (name)
  4462.                         self.entNPName.set_text (name)
  4463.                     elif (self.device.id or
  4464.                           (self.device.make_and_model and
  4465.                            self.device.make_and_model != "Unknown")):
  4466.                         if self.device.id:
  4467.                             id_dict = self.device.id_dict
  4468.                         else:
  4469.                             id_dict = {}
  4470.                             (id_dict["MFG"],
  4471.                              id_dict["MDL"]) = cupshelpers.ppds.\
  4472.                                  ppdMakeModelSplit (self.device.make_and_model)
  4473.                             id_dict["DES"] = ""
  4474.                             id_dict["CMD"] = []
  4475.  
  4476.                         (status, ppdname) = self.ppds.\
  4477.                             getPPDNameFromDeviceID (id_dict["MFG"],
  4478.                                                     id_dict["MDL"],
  4479.                                                     id_dict["DES"],
  4480.                                                     id_dict["CMD"],
  4481.                                                     self.device.uri,
  4482.                                                     self.jockey_installed_files)
  4483.                     else:
  4484.                         (status, ppdname) = self.ppds.\
  4485.                             getPPDNameFromDeviceID ("Generic",
  4486.                                                     "Printer",
  4487.                                                     "Generic Printer",
  4488.                                                     [],
  4489.                                                     self.device.uri)
  4490.  
  4491.                     if ppdname and not self.remotecupsqueue:
  4492.                         ppddict = self.ppds.getInfoFromPPDName (ppdname)
  4493.                         make_model = ppddict['ppd-make-and-model']
  4494.                         (make, model) = \
  4495.                             cupshelpers.ppds.ppdMakeModelSplit (make_model)
  4496.                         self.auto_make = make
  4497.                         self.auto_model = model
  4498.                         self.auto_driver = ppdname
  4499.                         if (status == self.ppds.STATUS_SUCCESS and \
  4500.                             self.dialog_mode != "ppd"):
  4501.                             self.exactdrivermatch = True
  4502.                         else:
  4503.                             self.exactdrivermatch = False
  4504.                 except:
  4505.                     nonfatalException ()
  4506.  
  4507.                 if not self.remotecupsqueue:
  4508.                     self.fillMakeList()
  4509.             elif page_nr == 3: # Model has been selected
  4510.                 if not self.device.id:
  4511.                     # Choose an appropriate name when no Device ID
  4512.                     # is available, based on the model the user has
  4513.                     # selected.
  4514.                     try:
  4515.                         model, iter = self.tvNPModels.get_selection ().\
  4516.                                       get_selected ()
  4517.                         name = model.get(iter, 0)[0]
  4518.                         name = self.mainapp.makeNameUnique (name)
  4519.                         self.entNPName.set_text (name)
  4520.                     except:
  4521.                         nonfatalException ()
  4522.  
  4523.             self.ready (self.NewPrinterWindow)
  4524.             if self.dialog_mode == "printer":
  4525.                 if self.remotecupsqueue:
  4526.                     order = [1, 0]
  4527.                 elif self.exactdrivermatch:
  4528.                     order = [1, 6, 0]
  4529.                 elif self.rbtnNPFoomatic.get_active():
  4530.                     order = [1, 2, 3, 6, 0]
  4531.                 elif self.rbtnNPPPD.get_active():
  4532.                     order = [1, 2, 6, 0]
  4533.                 else:
  4534.                     # Downloadable driver
  4535.                     order = [1, 2, 7, 6, 0]
  4536.             else:
  4537.                 if self.remotecupsqueue:
  4538.                     order = [0]
  4539.                 elif self.exactdrivermatch:
  4540.                     order = [6, 0]
  4541.                 elif self.rbtnNPFoomatic.get_active():
  4542.                     order = [2, 3, 6, 0]
  4543.                 elif self.rbtnNPPPD.get_active():
  4544.                     order = [2, 6, 0]
  4545.                 else:
  4546.                     # Downloadable driver
  4547.                     order = [2, 7, 6, 0]
  4548.         elif self.dialog_mode == "device":
  4549.             order = [1]
  4550.         elif self.dialog_mode == "ppd":
  4551.             if self.rbtnNPFoomatic.get_active():
  4552.                 order = [2, 3, 5, 6]
  4553.             elif self.rbtnNPPPD.get_active():
  4554.                 order = [2, 5, 6]
  4555.             else:
  4556.                 # Downloadable driver
  4557.                 order = [2, 7, 5, 6]
  4558.  
  4559.         next_page_nr = order[order.index(page_nr)+step]
  4560.  
  4561.         # fill Installable Options tab
  4562.         fetch_ppd = False
  4563.         try:
  4564.             if order.index (5) > -1:
  4565.                 # There is a copy settings page in this set
  4566.                 fetch_ppd = next_page_nr == 5 and step >= 0
  4567.         except ValueError:
  4568.             fetch_ppd = next_page_nr == 6 and step >= 0
  4569.  
  4570.         debugprint ("Will fetch ppd? %d" % fetch_ppd)
  4571.         if fetch_ppd:
  4572.             self.ppd = self.getNPPPD()
  4573.             self.installable_options = False
  4574.             if self.ppd == None:
  4575.                 return
  4576.  
  4577.             # Prepare Installable Options screen.
  4578.             if isinstance(self.ppd, cups.PPD):
  4579.                 self.fillNPInstallableOptions()
  4580.             else:
  4581.                 # Put a label there explaining why the page is empty.
  4582.                 ppd = self.ppd
  4583.                 self.ppd = None
  4584.                 self.fillNPInstallableOptions()
  4585.                 self.ppd = ppd
  4586.  
  4587.             if not self.installable_options:
  4588.                 if next_page_nr == 6:
  4589.                     # step over if empty
  4590.                     next_page_nr = order[order.index(next_page_nr)+1]
  4591.  
  4592.         # Step over empty Installable Options tab when moving backwards.
  4593.         if next_page_nr == 6 and not self.installable_options and step<0:
  4594.             next_page_nr = order[order.index(next_page_nr)-1]
  4595.  
  4596.         if step >= 0 and next_page_nr == 7: # About to show downloadable drivers
  4597.             if self.drivers_lock.locked ():
  4598.                 # Still searching for drivers.
  4599.                 self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  4600.                                          _('Searching') + '</span>\n\n' +
  4601.                                          _('Searching for drivers'))
  4602.                 self.WaitWindow.set_transient_for (self.NewPrinterWindow)
  4603.                 self.WaitWindow.show ()
  4604.                 self.busy (self.WaitWindow)
  4605.                 self.busy (self.NewPrinterWindow)
  4606.  
  4607.                 # Keep the UI refreshed while we wait for the drivers
  4608.                 # query to complete.
  4609.                 while self.drivers_lock.locked ():
  4610.                     while gtk.events_pending ():
  4611.                         gtk.main_iteration ()
  4612.                     time.sleep (0.1)
  4613.  
  4614.                 self.ready (self.NewPrinterWindow)
  4615.                 self.WaitWindow.hide ()
  4616.  
  4617.             self.fillDownloadableDrivers()
  4618.  
  4619.         if step >= 0 and next_page_nr == 0: # About to choose a name.
  4620.             # Suggest an appropriate name.
  4621.             name = None
  4622.             descr = None
  4623.  
  4624.             try:
  4625.                 if (self.device.id and
  4626.                     not self.device.type in ("socket", "lpd", "ipp",
  4627.                                              "http", "https", "bluetooth")):
  4628.                     name = "%s %s" % (self.device.id_dict["MFG"], 
  4629.                                       self.device.id_dict["MDL"])
  4630.                     descr = "%s %s" % (self.device.id_dict["MFG"],
  4631.                                        self.device.id_dict["MDL"])
  4632.             except:
  4633.                 nonfatalException ()
  4634.  
  4635.             try:
  4636.                 if name == None and isinstance (self.ppd, cups.PPD):
  4637.                     mname = self.ppd.findAttr ("modelName").value
  4638.                     make, model = cupshelpers.ppds.ppdMakeModelSplit (mname)
  4639.                     name = "%s %s" % (make, model)
  4640.                     descr = "%s %s" % (make, model)
  4641.             except:
  4642.                 nonfatalException ()
  4643.  
  4644.             if name == None:
  4645.                 name = 'printer'
  4646.  
  4647.             name = self.mainapp.makeNameUnique (name)
  4648.             self.entNPName.set_text (name)
  4649.  
  4650.             if self.entNPDescription.get_text () == '' and descr:
  4651.                 self.entNPDescription.set_text (descr)
  4652.  
  4653.         self.ntbkNewPrinter.set_current_page(next_page_nr)
  4654.  
  4655.         self.setNPButtons()
  4656.  
  4657.     def setNPButtons(self):
  4658.         nr = self.ntbkNewPrinter.get_current_page()
  4659.  
  4660.         if self.dialog_mode == "device":
  4661.             self.btnNPBack.hide()
  4662.             self.btnNPForward.hide()
  4663.             self.btnNPApply.show()
  4664.             try:
  4665.                 uri = self.getDeviceURI ()
  4666.                 valid = validDeviceURI (uri)
  4667.             except AttributeError:
  4668.                 # No device selected yet.
  4669.                 valid = False
  4670.             self.btnNPApply.set_sensitive (valid)
  4671.             return
  4672.  
  4673.         if self.dialog_mode == "ppd":
  4674.             if nr == 5: # Apply
  4675.                 if not self.installable_options:
  4676.                     # There are no installable options, so this is the
  4677.                     # last page.
  4678.                     debugprint ("No installable options")
  4679.                     self.btnNPForward.hide ()
  4680.                     self.btnNPApply.show ()
  4681.                 else:
  4682.                     self.btnNPForward.show ()
  4683.                     self.btnNPApply.hide ()
  4684.                 return
  4685.             elif nr == 6:
  4686.                 self.btnNPForward.hide()
  4687.                 self.btnNPApply.show()
  4688.                 return
  4689.             else:
  4690.                 self.btnNPForward.show()
  4691.                 self.btnNPApply.hide()
  4692.             if nr == 2:
  4693.                 self.btnNPBack.hide()
  4694.                 self.btnNPForward.show()
  4695.                 downloadable_selected = False
  4696.                 if self.rbtnNPDownloadableDriverSearch.get_active ():
  4697.                     combobox = self.cmbNPDownloadableDriverFoundPrinters
  4698.                     iter = combobox.get_active_iter ()
  4699.                     if iter and combobox.get_model ().get_value (iter, 1):
  4700.                         downloadable_selected = True
  4701.  
  4702.                 self.btnNPForward.set_sensitive(bool(
  4703.                         self.rbtnNPFoomatic.get_active() or
  4704.                         self.filechooserPPD.get_filename() or
  4705.                         downloadable_selected))
  4706.                 return
  4707.             else:
  4708.                 self.btnNPBack.show()
  4709.  
  4710.         # class/printer
  4711.  
  4712.         if nr == 1: # Device
  4713.             valid = False
  4714.             try:
  4715.                 uri = self.getDeviceURI ()
  4716.                 valid = validDeviceURI (uri)
  4717.             except:
  4718.                 pass
  4719.             self.btnNPForward.set_sensitive(valid)
  4720.             self.btnNPBack.hide ()
  4721.         else:
  4722.             self.btnNPBack.show()
  4723.  
  4724.         self.btnNPForward.show()
  4725.         self.btnNPApply.hide()
  4726.  
  4727.         if nr == 0: # Name
  4728.             self.btnNPBack.show()
  4729.             if self.dialog_mode == "printer" or \
  4730.                     self.dialog_mode == "printer_with_uri":
  4731.                 self.btnNPForward.hide()
  4732.                 self.btnNPApply.show()
  4733.                 self.btnNPApply.set_sensitive(
  4734.                     self.mainapp.checkNPName(self.entNPName.get_text()))
  4735.             if self.dialog_mode == "class":
  4736.                 # This is the first page for the New Class dialog, so
  4737.                 # hide the Back button.
  4738.                 self.btnNPBack.hide ()
  4739.             if self.dialog_mode == "printer_with_uri" and \
  4740.                     (self.remotecupsqueue or \
  4741.                          (self.exactdrivermatch and \
  4742.                               not self.installable_options)):
  4743.                 self.btnNPBack.hide ()
  4744.         if nr == 2: # Make/PPD file
  4745.             downloadable_selected = False
  4746.             if self.rbtnNPDownloadableDriverSearch.get_active ():
  4747.                 combobox = self.cmbNPDownloadableDriverFoundPrinters
  4748.                 iter = combobox.get_active_iter ()
  4749.                 if iter and combobox.get_model ().get_value (iter, 1):
  4750.                     downloadable_selected = True
  4751.  
  4752.             self.btnNPForward.set_sensitive(bool(
  4753.                 self.rbtnNPFoomatic.get_active() or
  4754.                 self.filechooserPPD.get_filename() or
  4755.                 downloadable_selected))
  4756.             # If we have an auto-detected printer for which there was no
  4757.             # driver found, we have already the URI and so this step is
  4758.             # not needed in the wizard. This makes manufacturer?PPD selection
  4759.             # the firts step
  4760.             if self.dialog_mode == "printer_with_uri":
  4761.                 self.btnNPBack.hide()
  4762.         if nr == 3: # Model/Driver
  4763.             model, iter = self.tvNPDrivers.get_selection().get_selected()
  4764.             self.btnNPForward.set_sensitive(bool(iter))
  4765.         if nr == 4: # Class Members
  4766.             self.btnNPForward.hide()
  4767.             self.btnNPApply.show()
  4768.             self.btnNPApply.set_sensitive(
  4769.                 bool(getCurrentClassMembers(self.tvNCMembers)))
  4770.         if nr == 6: # Installable options
  4771.             if self.dialog_mode == "printer_with_uri" and \
  4772.                     self.exactdrivermatch:
  4773.                 self.btnNPBack.hide ()
  4774.         if nr == 7: # Downloadable drivers
  4775.             if self.ntbkNPDownloadableDriverProperties.get_current_page() == 1:
  4776.                 accepted = self.rbtnNPDownloadLicenseYes.get_active ()
  4777.             else:
  4778.                 treeview = self.tvNPDownloadableDrivers
  4779.                 model, iter = treeview.get_selection ().get_selected ()
  4780.                 accepted = (iter != None)
  4781.  
  4782.             self.btnNPForward.set_sensitive(accepted)
  4783.  
  4784.     def on_entNPName_changed(self, widget):
  4785.         # restrict
  4786.         text = unicode (widget.get_text())
  4787.         new_text = text
  4788.         new_text = new_text.replace("/", "")
  4789.         new_text = new_text.replace("#", "")
  4790.         new_text = new_text.replace(" ", "")
  4791.         if text!=new_text:
  4792.             widget.set_text(new_text)
  4793.         if self.dialog_mode == "printer":
  4794.             self.btnNPApply.set_sensitive(
  4795.                 self.mainapp.checkNPName(new_text))
  4796.         else:
  4797.             self.btnNPForward.set_sensitive(
  4798.                 self.mainapp.checkNPName(new_text))
  4799.  
  4800.     def fetchDevices(self, network=False, current_uri=None):
  4801.         debugprint ("fetchDevices")
  4802.         self.inc_spinner_task ()
  4803.  
  4804.         network_schemes = ["dnssd", "snmp"]
  4805.         error_handler = self.error_getting_devices
  4806.         if network == False:
  4807.             reply_handler = (lambda x, y:
  4808.                                  self.local_devices_reply (x, y,
  4809.                                                            current_uri))
  4810.             cupshelpers.getDevices (self.fetchDevices_conn,
  4811.                                     exclude_schemes=network_schemes,
  4812.                                     reply_handler=reply_handler,
  4813.                                     error_handler=error_handler)
  4814.         else:
  4815.             reply_handler = (lambda x, y:
  4816.                                  self.network_devices_reply (x, y,
  4817.                                                              current_uri))
  4818.             cupshelpers.getDevices (self.fetchDevices_conn,
  4819.                                     include_schemes=network_schemes,
  4820.                                     reply_handler=reply_handler,
  4821.                                     error_handler=error_handler)
  4822.  
  4823.     def error_getting_devices (self, conn, exc):
  4824.         # Just ignore the error.
  4825.         debugprint ("Error fetching devices: %s" % repr (exc))
  4826.         if conn != self.fetchDevices_conn:
  4827.             return
  4828.  
  4829.         self.dec_spinner_task ()
  4830.         self.fetchDevices_conn._end_operation ()
  4831.         self.fetchDevices_conn.destroy ()
  4832.         self.fetchDevices_conn = None
  4833.  
  4834.     def local_devices_reply (self, conn, result, current_uri):
  4835.         if conn != self.fetchDevices_conn:
  4836.             return
  4837.  
  4838.         self.dec_spinner_task ()
  4839.  
  4840.         # Now we've got the local devices, start a request for the
  4841.         # network devices.
  4842.         self.fetchDevices (network=True, current_uri=current_uri)
  4843.  
  4844.         # Add the local devices to the list.
  4845.         self.add_devices (result, current_uri)
  4846.  
  4847.     def network_devices_reply (self, conn, result, current_uri):
  4848.         if conn != self.fetchDevices_conn:
  4849.             return
  4850.  
  4851.         self.dec_spinner_task ()
  4852.         self.fetchDevices_conn._end_operation ()
  4853.         self.fetchDevices_conn.destroy ()
  4854.         self.fetchDevices_conn = None
  4855.  
  4856.         # Add the network devices to the list.
  4857.         no_more = True
  4858.         need_resolving = {}
  4859.         for uri, device in result.iteritems ():
  4860.             if uri.startswith ("dnssd://"):
  4861.                 need_resolving[uri] = device
  4862.                 no_more = False
  4863.  
  4864.         for uri in need_resolving.keys ():
  4865.             del result[uri]
  4866.  
  4867.         self.add_devices (result, current_uri, no_more=no_more)
  4868.  
  4869.         if len (need_resolving) > 0:
  4870.             resolver = dnssdresolve.DNSSDHostNamesResolver (need_resolving)
  4871.             resolver.resolve (reply_handler=lambda devices:
  4872.                                   self.dnssd_resolve_reply (current_uri,
  4873.                                                             devices))
  4874.  
  4875.     def dnssd_resolve_reply (self, current_uri, devices):
  4876.         self.add_devices (devices, current_uri, no_more=True)
  4877.  
  4878.     def get_hpfax_device_id(self, faxuri):
  4879.         os.environ["URI"] = faxuri
  4880.         cmd = 'LC_ALL=C DISPLAY= hp-info -x -i -d"${URI}"'
  4881.         debugprint (faxuri + ": " + cmd)
  4882.         try:
  4883.             p = subprocess.Popen (cmd, shell=True, close_fds=True,
  4884.                                   stdin=file("/dev/null"),
  4885.                                   stdout=subprocess.PIPE,
  4886.                                   stderr=subprocess.PIPE)
  4887.             (stdout, stderr) = p.communicate ()
  4888.         except:
  4889.             # Problem executing command.
  4890.             return None
  4891.  
  4892.         faxtype = -1
  4893.         for line in stdout.split ("\n"):
  4894.             if line.find ("fax-type") == -1:
  4895.                 continue
  4896.             res = re.search ("(\d+)", line)
  4897.             if res:
  4898.                 resg = res.groups()
  4899.                 faxtype = resg[0]
  4900.             if faxtype >= 0:
  4901.                 break
  4902.         if faxtype <= 0:
  4903.             return None
  4904.         elif faxtype == 4:
  4905.             return 'MFG:HP;MDL:Fax 2;DES:HP Fax 2;'
  4906.         else:
  4907.             return 'MFG:HP;MDL:Fax;DES:HP Fax;'
  4908.  
  4909.     def get_hplip_uri_for_network_printer(self, host, mode):
  4910.         os.environ["HOST"] = host
  4911.         if mode == "print": mod = "-c"
  4912.         elif mode == "fax": mod = "-f"
  4913.         else: mod = "-c"
  4914.         cmd = 'hp-makeuri ' + mod + ' "${HOST}"'
  4915.         debugprint (host + ": " + cmd)
  4916.         uri = None
  4917.         try:
  4918.             p = subprocess.Popen (cmd, shell=True, close_fds=True,
  4919.                                   stdin=file("/dev/null"),
  4920.                                   stdout=subprocess.PIPE,
  4921.                                   stderr=subprocess.PIPE)
  4922.             (stdout, stderr) = p.communicate ()
  4923.             if p.returncode != 0:
  4924.                 return None
  4925.         except:
  4926.             # Problem executing command.
  4927.             return None
  4928.  
  4929.         uri = stdout.strip ()
  4930.         return uri
  4931.  
  4932.     def getNetworkPrinterMakeModel(self, host=None, device=None):
  4933.         """
  4934.         Try to determine the make and model for the currently selected
  4935.         network printer, and store this in the data structure for the
  4936.         printer.
  4937.         Returns (hostname or None, uri or None).
  4938.         """
  4939.         uri = None
  4940.         if device == None:
  4941.             device = self.device
  4942.         # Determine host name/IP
  4943.         if host == None:
  4944.             s = device.uri.find ("://")
  4945.             if s != -1:
  4946.                 s += 3
  4947.                 e = device.uri[s:].find (":")
  4948.                 if e == -1: e = device.uri[s:].find ("/")
  4949.                 if e == -1: e = device.uri[s:].find ("?")
  4950.                 if e == -1: e = len (device.uri)
  4951.                 host = device.uri[s:s+e]
  4952.         # Try to get make and model via SNMP
  4953.         if host:
  4954.             os.environ["HOST"] = host
  4955.             cmd = '/usr/lib/cups/backend/snmp "${HOST}"'
  4956.             debugprint (host + ": " + cmd)
  4957.             stdout = None
  4958.             try:
  4959.                 p = subprocess.Popen (cmd, shell=True, close_fds=True,
  4960.                                       stdin=file("/dev/null"),
  4961.                                       stdout=subprocess.PIPE,
  4962.                                       stderr=subprocess.PIPE)
  4963.                 (stdout, stderr) = p.communicate ()
  4964.                 if p.returncode != 0:
  4965.                     stdout = None
  4966.             except:
  4967.                 # Problem executing command.
  4968.                 pass
  4969.  
  4970.             if stdout != None:
  4971.                 uri = re.sub("^\s*\S+\s+", "", stdout)
  4972.                 uri = re.sub("\s.*$", "", uri)
  4973.                 mm = re.sub("^\s*\S+\s+\S+\s+\"", "", stdout)
  4974.                 mm = re.sub("\"\s+.*$", "", mm)
  4975.                 if mm and mm != "": device.make_and_model = mm
  4976.                 location = re.sub("^\s*(\S+\s+){2}(\".*\"\s+){3}\"", "", stdout)
  4977.                 device.location = re.sub("\"\s*$", "", location)
  4978.  
  4979.         # Extract make and model and create a pseudo device ID, so
  4980.         # that a PPD/driver can be assigned to the device
  4981.         make_and_model = None
  4982.         if len (device.make_and_model) > 7:
  4983.             make_and_model = device.make_and_model
  4984.         elif len (device.info) > 7:
  4985.             make_and_model = device.info
  4986.             make_and_model = re.sub("\s*(\(|\d+\.\d+\.\d+\.\d+).*$", "", make_and_model)
  4987.         if make_and_model and not device.id:
  4988.             mk = None
  4989.             md = None
  4990.             (mk, md) = cupshelpers.ppds.ppdMakeModelSplit (make_and_model)
  4991.             device.id = "MFG:" + mk + ";MDL:" + md + ";DES:" + mk + " " + md + ";"
  4992.             device.id_dict = cupshelpers.parseDeviceID (device.id)
  4993.             device.make_and_model = "%s %s" % (mk, md)
  4994.             device.info = device.make_and_model
  4995.  
  4996.         return (host, uri)
  4997.  
  4998.     def fillDeviceTab(self, current_uri=None):
  4999.         self.device_selected = -1
  5000.         model = gtk.TreeStore (gobject.TYPE_STRING,   # device-info
  5001.                                gobject.TYPE_PYOBJECT, # PhysicalDevice obj
  5002.                                gobject.TYPE_BOOLEAN)  # Separator?
  5003.         other = cupshelpers.Device('', **{'device-info' :_("Other")})
  5004.         physother = PhysicalDevice (other)
  5005.         self.devices = [physother]
  5006.         model.append (None, row=[physother.get_info (), physother, False])
  5007.         network_iter = model.append (None, row=[_("Network Printer"),
  5008.                                                 None,
  5009.                                                 False])
  5010.         network_dict = { 'device-class': 'network',
  5011.                          'device-info': _("Find Network Printer") }
  5012.         network = cupshelpers.Device ('network', **network_dict)
  5013.         find_nw_iter = model.append (network_iter,
  5014.                                      row=[network_dict['device-info'],
  5015.                                           PhysicalDevice (network), False])
  5016.         model.insert_after (network_iter, find_nw_iter, row=['', None, True])
  5017.         self.devices_find_nw_iter = find_nw_iter
  5018.         self.devices_network_iter = network_iter
  5019.         self.devices_network_fetched = False
  5020.         self.tvNPDevices.set_model (model)
  5021.         self.entNPTDevice.set_text ('')
  5022.         self.expNPDeviceURIs.hide ()
  5023.         column = self.tvNPDevices.get_column (0)
  5024.         self.tvNPDevices.set_cursor ((0,), column)
  5025.  
  5026.         allowed = True
  5027.         try:
  5028.             if (self.mainapp.connect_server == 'localhost' or
  5029.                 self.mainapp.connect_server[0] == '/'):
  5030.                 f = firewall.Firewall ()
  5031.                 ipp_allowed = f.check_ipp_client_allowed ()
  5032.                 mdns_allowed = f.check_mdns_allowed ()
  5033.                 snmp_allowed = f.check_snmp_allowed ()
  5034.                 allowed = (ipp_allowed and mdns_allowed and snmp_allowed)
  5035.             else:
  5036.                 # This is a remote server.  Nothing we can do about
  5037.                 # the firewall there.
  5038.                 ipp_allowed = mdns_allowed = snmp_allowed = allowed = True
  5039.  
  5040.             secondary_text = _("The firewall may need adjusting in order to "
  5041.                                "detect network printers.  Adjust the "
  5042.                                "firewall now?") + "\n\n"
  5043.             if not ipp_allowed:
  5044.                 secondary_text += ("- " +
  5045.                                    _("Allow all incoming IPP Browse packets") +
  5046.                                    "\n")
  5047.                 f.add_rule (f.ALLOW_IPP_CLIENT)
  5048.             if not mdns_allowed:
  5049.                 secondary_text += ("- " +
  5050.                                    _("Allow all incoming mDNS traffic") + "\n")
  5051.                 f.add_rule (f.ALLOW_MDNS)
  5052.             if not snmp_allowed:
  5053.                 secondary_text += ("- " +
  5054.                                    _("Allow all responses to "
  5055.                                      "SNMP broadcast queries") + "\n")
  5056.                 f.add_rule (f.ALLOW_SNMP)
  5057.  
  5058.             if not allowed:
  5059.                 dialog = gtk.MessageDialog (self.mainapp.PrintersWindow,
  5060.                                             gtk.DIALOG_MODAL |
  5061.                                             gtk.DIALOG_DESTROY_WITH_PARENT,
  5062.                                             gtk.MESSAGE_QUESTION,
  5063.                                             gtk.BUTTONS_NONE,
  5064.                                             _("Adjust Firewall"))
  5065.                 dialog.format_secondary_markup (secondary_text)
  5066.                 dialog.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
  5067.                                     _("Adjust Firewall"), gtk.RESPONSE_YES)
  5068.                 response = dialog.run ()
  5069.                 dialog.destroy ()
  5070.  
  5071.                 if response == gtk.RESPONSE_YES:
  5072.                     f.add_rule (f.ALLOW_IPP_SERVER)
  5073.                     f.write ()
  5074.         except (dbus.DBusException, Exception):
  5075.             nonfatalException ()
  5076.  
  5077.         self.fetchDevices_conn = asyncconn.Connection ()
  5078.         self.fetchDevices_conn._begin_operation (_("fetching device list"))
  5079.         self.fetchDevices (network=False, current_uri=current_uri)
  5080.  
  5081.     def add_devices (self, devices, current_uri, no_more=False):
  5082.         if current_uri:
  5083.             if devices.has_key (current_uri):
  5084.                 current = devices.pop(current_uri)
  5085.             elif devices.has_key (current_uri.replace (":9100", "")):
  5086.                 current_uri = current_uri.replace (":9100", "")
  5087.                 current = devices.pop(current_uri)
  5088.             elif no_more:
  5089.                 current = cupshelpers.Device (current_uri)
  5090.                 current.info = "Current device"
  5091.             else:
  5092.                 current_uri = None
  5093.  
  5094.         devices = devices.values()
  5095.  
  5096.         for device in devices:
  5097.             if device.type == "socket":
  5098.                 # Remove default port to more easily find duplicate URIs
  5099.                 device.uri = device.uri.replace (":9100", "")
  5100.  
  5101.         # Map generic URIs to something canonical
  5102.         def replace_generic (device):
  5103.             if device.uri == "hp:/no_device_found":
  5104.                 device.uri = "hp"
  5105.             elif device.uri == "hpfax:/no_device_found":
  5106.                 device.uri = "hpfax"
  5107.             return device
  5108.  
  5109.         devices = map (replace_generic, devices)
  5110.  
  5111.         # Mark duplicate URIs for deletion
  5112.         for i in range (len (devices) - 1):
  5113.             for j in range (i + 1, len (devices)):
  5114.                 device1 = devices[i]
  5115.                 device2 = devices[j]
  5116.                 if device1.uri == "delete" or device2.uri == "delete":
  5117.                     continue
  5118.                 if device1.uri == device2.uri:
  5119.                     # Keep the one with the longer (better) device ID
  5120.                     if (not device1.id):
  5121.                         device1.uri = "delete"
  5122.                     elif (not device2.id):
  5123.                         device2.uri = "delete"
  5124.                     elif (len (device1.id) < len (device2.id)):
  5125.                         device1.uri = "delete"
  5126.                     else:
  5127.                         device2.uri = "delete"
  5128.         devices = filter(lambda x: x.uri not in ("hp", "hpfax",
  5129.                                                  "hal", "beh",
  5130.                                                  "scsi", "http", "delete"),
  5131.                          devices)
  5132.         newdevices = []
  5133.         for device in devices:
  5134.             physicaldevice = PhysicalDevice (device)
  5135.             try:
  5136.                 i = self.devices.index (physicaldevice)
  5137.                 self.devices[i].add_device (device)
  5138.             except ValueError:
  5139.                 self.devices.append (physicaldevice)
  5140.                 newdevices.append (physicaldevice)
  5141.  
  5142.         self.devices.sort()
  5143.         if current_uri:
  5144.             current_device = PhysicalDevice (current)
  5145.             try:
  5146.                 i = self.devices.index (current_device)
  5147.                 self.devices[i].add_device (current)
  5148.                 current_device = self.devices[i]
  5149.             except ValueError:
  5150.                 self.devices.append (current_device)
  5151.                 newdevices.append (current_device)
  5152.         else:
  5153.             current_device = None
  5154.  
  5155.         model = self.tvNPDevices.get_model ()
  5156.  
  5157.         network_iter = self.devices_network_iter
  5158.         find_nw_iter = self.devices_find_nw_iter
  5159.         for device in newdevices:
  5160.             devs = device.get_devices ()
  5161.             network = devs[0].device_class == 'network'
  5162.             info = device.get_info ()
  5163.             if device == current_device:
  5164.                 info += _(" (Current)")
  5165.             row=[info, device, False]
  5166.             if network:
  5167.                 if devs[0].uri != devs[0].type:
  5168.                     # An actual network printer device.  Put this at the top.
  5169.                     iter = model.insert_before (network_iter, find_nw_iter,
  5170.                                                 row=row)
  5171.  
  5172.                     # If this is the currently selected device we need
  5173.                     # to expand the "Network Printer" row so that it
  5174.                     # is visible.
  5175.                     if device == current_device:
  5176.                         network_path = model.get_path (network_iter)
  5177.                         self.tvNPDevices.expand_row (network_path, False)
  5178.                 else:
  5179.                     # Just a method of finding one.
  5180.                     iter = model.append (network_iter, row=row)
  5181.             else:
  5182.                 # Insert this local device in order.
  5183.                 iter = model.get_iter_first ()
  5184.                 while iter != network_iter:
  5185.                     physdev = model.get_value (iter, 1)
  5186.                     if physdev > device:
  5187.                         break
  5188.  
  5189.                     iter = model.iter_next (iter)
  5190.  
  5191.                 iter = model.insert_before (None, iter, row=row)
  5192.  
  5193.             if device == current_device:
  5194.                 device_select_path = model.get_path (iter)
  5195.                 self.tvNPDevices.scroll_to_cell (device_select_path,
  5196.                                                  row_align=0.5)
  5197.                 column = self.tvNPDevices.get_column (0)
  5198.                 self.tvNPDevices.set_cursor (device_select_path, column)
  5199.  
  5200.         connection_select_path = 0
  5201.         if current_uri:
  5202.             model = self.tvNPDeviceURIs.get_model ()
  5203.             iter = model.get_iter_first ()
  5204.             i = 0
  5205.             while iter:
  5206.                 dev = model.get_value (iter, 1)
  5207.                 if current_uri == dev.uri:
  5208.                     connection_select_path = i
  5209.                     break
  5210.  
  5211.                 iter = model.iter_next (iter)
  5212.                 i += 1
  5213.         elif not self.device_selected:
  5214.             # Select the device.
  5215.             column = self.tvNPDevices.get_column (0)
  5216.             self.tvNPDevices.set_cursor ((0,), column)
  5217.  
  5218.             # Select the connection.
  5219.             column = self.tvNPDeviceURIs.get_column (0)
  5220.             self.tvNPDeviceURIs.set_cursor (connection_select_path, column)
  5221.  
  5222.     def on_entNPTDevice_changed(self, entry):
  5223.         self.setNPButtons()
  5224.  
  5225.     ## SMB browsing
  5226.  
  5227.     def browse_smb_hosts(self):
  5228.         """Initialise the SMB tree store."""
  5229.         store = self.smb_store
  5230.         store.clear ()
  5231.         self.busy(self.SMBBrowseDialog)
  5232.         class X:
  5233.             pass
  5234.         dummy = X()
  5235.         dummy.smbc_type = pysmb.smbc.PRINTER_SHARE
  5236.         dummy.name = _('Scanning...')
  5237.         dummy.comment = ''
  5238.         store.append(None, [dummy])
  5239.         while gtk.events_pending ():
  5240.             gtk.main_iteration ()
  5241.  
  5242.         debug = 0
  5243.         if get_debugging ():
  5244.             debug = 10
  5245.         smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
  5246.         ctx = pysmb.smbc.Context (debug=debug,
  5247.                                   auth_fn=smbc_auth.callback)
  5248.         entries = None
  5249.         try:
  5250.             while smbc_auth.perform_authentication () > 0:
  5251.                 try:
  5252.                     entries = ctx.opendir ("smb://").getdents ()
  5253.                 except Exception, e:
  5254.                     smbc_auth.failed (e)
  5255.         except RuntimeError, (e, s):
  5256.             if e != errno.ENOENT:
  5257.                 debugprint ("Runtime error: %s" % repr ((e, s)))
  5258.         except:
  5259.             nonfatalException ()
  5260.  
  5261.         store.clear ()
  5262.         if entries:
  5263.             for entry in entries:
  5264.                 if entry.smbc_type in [pysmb.smbc.WORKGROUP,
  5265.                                        pysmb.smbc.SERVER]:
  5266.                     iter = store.append (None, [entry])
  5267.                     i = store.append (iter)
  5268.  
  5269.         specified_uri = SMBURI (uri=self.entSMBURI.get_text ())
  5270.         (group, host, share, user, password) = specified_uri.separate ()
  5271.         if len (host) > 0:
  5272.             # The user has specified a server before clicking Browse.
  5273.             # Append the server as a top-level entry.
  5274.             class FakeEntry:
  5275.                 pass
  5276.             toplevel = FakeEntry ()
  5277.             toplevel.smbc_type = pysmb.smbc.SERVER
  5278.             toplevel.name = host
  5279.             toplevel.comment = ''
  5280.             iter = store.append (None, [toplevel])
  5281.             i = store.append (iter)
  5282.  
  5283.             # Now expand it.
  5284.             path = store.get_path (iter)
  5285.             self.tvSMBBrowser.expand_row (path, 0)
  5286.  
  5287.         self.ready(self.SMBBrowseDialog)
  5288.  
  5289.         if store.get_iter_first () == None:
  5290.             self.SMBBrowseDialog.hide ()
  5291.             show_info_dialog (_("No Print Shares"),
  5292.                               _("There were no print shares found.  "
  5293.                                 "Please check that the Samba service is "
  5294.                                 "marked as trusted in your firewall "
  5295.                                 "configuration.") + '\n\n' +
  5296.                               TEXT_start_firewall_tool,
  5297.                               parent=self.NewPrinterWindow)
  5298.  
  5299.     def smb_select_function (self, path):
  5300.         """Don't allow this path to be selected unless it is a leaf."""
  5301.         iter = self.smb_store.get_iter (path)
  5302.         return not self.smb_store.iter_has_child (iter)
  5303.  
  5304.     def smbbrowser_cell_share (self, column, cell, model, iter):
  5305.         entry = model.get_value (iter, 0)
  5306.         share = ''
  5307.         if entry != None:
  5308.             share = entry.name
  5309.         cell.set_property ('text', share)
  5310.  
  5311.     def smbbrowser_cell_comment (self, column, cell, model, iter):
  5312.         entry = model.get_value (iter, 0)
  5313.         comment = ''
  5314.         if entry != None:
  5315.             comment = entry.comment
  5316.         cell.set_property ('text', comment)
  5317.  
  5318.     def on_tvSMBBrowser_row_activated (self, view, path, column):
  5319.         """Handle double-clicks in the SMB tree view."""
  5320.         store = self.smb_store
  5321.         iter = store.get_iter (path)
  5322.         entry = store.get_value (iter, 0)
  5323.         if entry and entry.smbc_type == pysmb.smbc.PRINTER_SHARE:
  5324.             # This is a share, not a host.
  5325.             self.btnSMBBrowseOk.clicked ()
  5326.             return
  5327.  
  5328.         if view.row_expanded (path):
  5329.             view.collapse_row (path)
  5330.         else:
  5331.             self.on_tvSMBBrowser_row_expanded (view, iter, path)
  5332.  
  5333.     def on_tvSMBBrowser_row_expanded (self, view, iter, path):
  5334.         """Handler for expanding a row in the SMB tree view."""
  5335.         model = view.get_model ()
  5336.         entry = model.get_value (iter, 0)
  5337.         if entry == None:
  5338.             return
  5339.  
  5340.         if entry.smbc_type == pysmb.smbc.WORKGROUP:
  5341.             # Workgroup
  5342.             # Be careful though: if there is a server with the
  5343.             # same name as the workgroup we will get a list of its
  5344.             # shares, not the workgroup's servers.
  5345.             try:
  5346.                 if self.expanding_row:
  5347.                     return
  5348.             except:
  5349.                 self.expanding_row = 1
  5350.  
  5351.             self.busy (self.SMBBrowseDialog)
  5352.             uri = "smb://%s/" % entry.name
  5353.             debug = 0
  5354.             if get_debugging ():
  5355.                 debug = 10
  5356.             smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
  5357.             ctx = pysmb.smbc.Context (debug=debug,
  5358.                                       auth_fn=smbc_auth.callback)
  5359.             entries = []
  5360.             try:
  5361.                 while smbc_auth.perform_authentication () > 0:
  5362.                     try:
  5363.                         entries = ctx.opendir (uri).getdents ()
  5364.                     except Exception, e:
  5365.                         smbc_auth.failed (e)
  5366.             except RuntimeError, (e, s):
  5367.                 if e != errno.ENOENT:
  5368.                     debugprint ("Runtime error: %s" % repr ((e, s)))
  5369.             except:
  5370.                 nonfatalException()
  5371.  
  5372.             while model.iter_has_child (iter):
  5373.                 i = model.iter_nth_child (iter, 0)
  5374.                 model.remove (i)
  5375.  
  5376.             for entry in entries:
  5377.                 if entry.smbc_type in [pysmb.smbc.SERVER,
  5378.                                        pysmb.smbc.PRINTER_SHARE]:
  5379.                     i = model.append (iter, [entry])
  5380.                 if entry.smbc_type == pysmb.smbc.SERVER:
  5381.                     n = model.append (i)
  5382.  
  5383.             view.expand_row (path, 0)
  5384.             del self.expanding_row
  5385.             self.ready (self.SMBBrowseDialog)
  5386.  
  5387.         elif entry.smbc_type == pysmb.smbc.SERVER:
  5388.             # Server
  5389.             try:
  5390.                 if self.expanding_row:
  5391.                     return
  5392.             except:
  5393.                 self.expanding_row = 1
  5394.  
  5395.             self.busy (self.SMBBrowseDialog)
  5396.             uri = "smb://%s/" % entry.name
  5397.             debug = 0
  5398.             if get_debugging ():
  5399.                 debug = 10
  5400.             smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
  5401.             ctx = pysmb.smbc.Context (debug=debug,
  5402.                                       auth_fn=smbc_auth.callback)
  5403.             shares = []
  5404.             try:
  5405.                 while smbc_auth.perform_authentication () > 0:
  5406.                     try:
  5407.                         shares = ctx.opendir (uri).getdents ()
  5408.                     except Exception, e:
  5409.                         smbc_auth.failed (e)
  5410.             except RuntimeError, (e, s):
  5411.                 if e != errno.EACCES and e != errno.EPERM:
  5412.                     debugprint ("Runtime error: %s" % repr ((e, s)))
  5413.             except:
  5414.                 nonfatalException()
  5415.  
  5416.             while model.iter_has_child (iter):
  5417.                 i = model.iter_nth_child (iter, 0)
  5418.                 model.remove (i)
  5419.  
  5420.             for share in shares:
  5421.                 if share.smbc_type == pysmb.smbc.PRINTER_SHARE:
  5422.                     i = model.append (iter, [share])
  5423.                     debugprint (repr (share))
  5424.  
  5425.             view.expand_row (path, 0)
  5426.             del self.expanding_row
  5427.             self.ready (self.SMBBrowseDialog)
  5428.  
  5429.     def on_entSMBURI_changed (self, ent):
  5430.         uri = ent.get_text ()
  5431.         (group, host, share, user, password) = SMBURI (uri=uri).separate ()
  5432.         if user:
  5433.             self.entSMBUsername.set_text (user)
  5434.         if password:
  5435.             self.entSMBPassword.set_text (password)
  5436.         if user or password:
  5437.             uri = SMBURI (group=group, host=host, share=share).get_uri ()
  5438.             ent.set_text(uri)
  5439.             self.rbtnSMBAuthSet.set_active(True)
  5440.         elif self.entSMBUsername.get_text () == '':
  5441.             self.rbtnSMBAuthPrompt.set_active(True)
  5442.  
  5443.         self.btnSMBVerify.set_sensitive(bool(uri))
  5444.         self.setNPButtons ()
  5445.  
  5446.     def on_tvSMBBrowser_cursor_changed(self, widget):
  5447.         store, iter = self.tvSMBBrowser.get_selection().get_selected()
  5448.         is_share = False
  5449.         if iter:
  5450.             entry = store.get_value (iter, 0)
  5451.             if entry:
  5452.                 is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE
  5453.  
  5454.         self.btnSMBBrowseOk.set_sensitive(iter != None and is_share)
  5455.  
  5456.     def on_btnSMBBrowse_clicked(self, button):
  5457.         self.btnSMBBrowseOk.set_sensitive(False)
  5458.  
  5459.         try:
  5460.             # Note: we do the browsing from *this* machine, regardless
  5461.             # of which CUPS server we are connected to.
  5462.             f = firewall.Firewall ()
  5463.             allowed = f.check_samba_client_allowed ()
  5464.         except:
  5465.             allowed = False
  5466.  
  5467.         if not allowed:
  5468.             show_info_dialog (_("Review Firewall"),
  5469.                               _("You may need to adjust the firewall "
  5470.                                 "to allow network printer discovery on this "
  5471.                                 "computer.") + '\n\n' +
  5472.                               TEXT_start_firewall_tool,
  5473.                               parent=self.NewPrinterWindow)
  5474.  
  5475.         self.SMBBrowseDialog.show()
  5476.         self.browse_smb_hosts()
  5477.  
  5478.     def on_btnSMBBrowseOk_clicked(self, button):
  5479.         store, iter = self.tvSMBBrowser.get_selection().get_selected()
  5480.         is_share = False
  5481.         if iter:
  5482.             entry = store.get_value (iter, 0)
  5483.             if entry:
  5484.                 is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE
  5485.  
  5486.         if not iter or not is_share:
  5487.             self.SMBBrowseDialog.hide()
  5488.             return
  5489.  
  5490.         parent_iter = store.iter_parent (iter)
  5491.         domain_iter = store.iter_parent (parent_iter)
  5492.         share = store.get_value (iter, 0)
  5493.         host = store.get_value (parent_iter, 0)
  5494.         if domain_iter:
  5495.             group = store.get_value (domain_iter, 0).name
  5496.         else:
  5497.             group = ''
  5498.         uri = SMBURI (group=group,
  5499.                       host=host.name,
  5500.                       share=share.name).get_uri ()
  5501.  
  5502.         self.entSMBUsername.set_text ('')
  5503.         self.entSMBPassword.set_text ('')
  5504.         self.entSMBURI.set_text (uri)
  5505.  
  5506.         self.SMBBrowseDialog.hide()
  5507.  
  5508.     def on_btnSMBBrowseCancel_clicked(self, widget, *args):
  5509.         self.SMBBrowseDialog.hide()
  5510.  
  5511.     def on_btnSMBBrowseRefresh_clicked(self, button):
  5512.         self.browse_smb_hosts()
  5513.  
  5514.     def on_rbtnSMBAuthSet_toggled(self, widget):
  5515.         self.tblSMBAuth.set_sensitive(widget.get_active())
  5516.  
  5517.     def on_btnSMBVerify_clicked(self, button):
  5518.         uri = self.entSMBURI.get_text ()
  5519.         (group, host, share, u, p) = SMBURI (uri=uri).separate ()
  5520.         user = ''
  5521.         passwd = ''
  5522.         reason = None
  5523.         auth_set = self.rbtnSMBAuthSet.get_active()
  5524.         if auth_set:
  5525.             user = self.entSMBUsername.get_text ()
  5526.             passwd = self.entSMBPassword.get_text ()
  5527.  
  5528.         accessible = False
  5529.         canceled = False
  5530.         self.busy ()
  5531.         try:
  5532.             debug = 0
  5533.             if get_debugging ():
  5534.                 debug = 10
  5535.  
  5536.             if auth_set:
  5537.                 # No prompting.
  5538.                 def do_auth (svr, shr, wg, un, pw):
  5539.                     return (group, user, passwd)
  5540.                 ctx = pysmb.smbc.Context (debug=debug, auth_fn=do_auth)
  5541.                 f = ctx.open ("smb://%s/%s" % (host, share),
  5542.                               os.O_RDWR, 0777)
  5543.                 accessible = True
  5544.             else:
  5545.                 # May need to prompt.
  5546.                 smbc_auth = pysmb.AuthContext (self.NewPrinterWindow,
  5547.                                                workgroup=group,
  5548.                                                user=user,
  5549.                                                passwd=passwd)
  5550.                 ctx = pysmb.smbc.Context (debug=debug,
  5551.                                           auth_fn=smbc_auth.callback)
  5552.                 while smbc_auth.perform_authentication () > 0:
  5553.                     try:
  5554.                         f = ctx.open ("smb://%s/%s" % (host, share),
  5555.                                       os.O_RDWR, 0777)
  5556.                         accessible = True
  5557.                     except Exception, e:
  5558.                         smbc_auth.failed (e)
  5559.  
  5560.                 if not accessible:
  5561.                     canceled = True
  5562.         except RuntimeError, (e, s):
  5563.             debugprint ("Error accessing share: %s" % repr ((e, s)))
  5564.             reason = s
  5565.         except:
  5566.             nonfatalException()
  5567.         self.ready ()
  5568.  
  5569.         if accessible:
  5570.             show_info_dialog (_("Print Share Verified"),
  5571.                               _("This print share is accessible."),
  5572.                               parent=self.NewPrinterWindow)
  5573.             return
  5574.  
  5575.         if not canceled:
  5576.             text = _("This print share is not accessible.")
  5577.             if reason:
  5578.                 text = reason
  5579.             show_error_dialog (_("Print Share Inaccessible"), text,
  5580.                                parent=self.NewPrinterWindow)
  5581.  
  5582.     ### IPP Browsing
  5583.     def update_IPP_URI_label(self):
  5584.         hostname = self.entNPTIPPHostname.get_text ()
  5585.         queue = self.entNPTIPPQueuename.get_text ()
  5586.         valid = len (hostname) > 0 and queue != '/printers/'
  5587.  
  5588.         if valid:
  5589.             uri = "%s://%s%s" % (self.device.type, hostname, queue)
  5590.             self.lblIPPURI.set_text (uri)
  5591.             self.lblIPPURI.show ()
  5592.             self.entNPTIPPQueuename.show ()
  5593.         else:
  5594.             self.lblIPPURI.hide ()
  5595.  
  5596.         self.btnIPPVerify.set_sensitive (valid)
  5597.         self.setNPButtons ()
  5598.  
  5599.     def on_entNPTIPPHostname_changed(self, ent):
  5600.         valid = len (ent.get_text ()) > 0
  5601.         self.update_IPP_URI_label ()
  5602.  
  5603.     def on_entNPTIPPQueuename_changed(self, ent):
  5604.         self.update_IPP_URI_label ()
  5605.  
  5606.     def on_btnIPPVerify_clicked(self, button):
  5607.         uri = self.lblIPPURI.get_text ()
  5608.         (scheme, rest) = urllib.splittype (uri)
  5609.         (hostport, rest) = urllib.splithost (rest)
  5610.         verified = False
  5611.         if hostport != None:
  5612.             (host, port) = urllib.splitnport (hostport, defport=631)
  5613.             if uri.startswith ("https:"):
  5614.                 encryption = cups.HTTP_ENCRYPT_ALWAYS
  5615.             else:
  5616.                 encryption = cups.HTTP_ENCRYPT_IF_REQUESTED
  5617.  
  5618.             def get_attributes():
  5619.                 c = cups.Connection (host=host, port=port,
  5620.                                      encryption=encryption)
  5621.                 return c.getPrinterAttributes (uri=uri)
  5622.                 
  5623.             op = TimedOperation (get_attributes)
  5624.             self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  5625.                                      _('Verifying') + '</span>\n\n' +
  5626.                                      _('Verifying printer'))
  5627.             self.WaitWindow.set_transient_for (self.NewPrinterWindow)
  5628.             self.WaitWindow.show ()
  5629.             self.busy (self.WaitWindow)
  5630.             source = gobject.timeout_add_seconds (10, op.cancel)
  5631.             try:
  5632.                 attributes = op.run ()
  5633.                 verified = True
  5634.             except OperationCanceled:
  5635.                 pass
  5636.             except cups.IPPError, (e, msg):
  5637.                 debugprint ("Failed to get attributes: %s (%d)" % (msg, e))
  5638.             except:
  5639.                 nonfatalException ()
  5640.  
  5641.             gobject.source_remove (source)
  5642.             self.WaitWindow.hide ()
  5643.         else:
  5644.             debugprint (uri)
  5645.  
  5646.         if verified:
  5647.             show_info_dialog (_("Print Share Verified"),
  5648.                               _("This print share is accessible."),
  5649.                               parent=self.NewPrinterWindow)
  5650.         else:
  5651.             show_error_dialog (_("Inaccessible"),
  5652.                                _("This print share is not accessible."),
  5653.                                self.NewPrinterWindow)
  5654.  
  5655.     def on_expNPDeviceURIs_expanded (self, widget, UNUSED):
  5656.         # When the expanded is not expanded we want its packing to be
  5657.         # 'expand = false' so that it aligns at the bottom (it packs
  5658.         # to the end of its vbox).  But when it is expanded we'd like
  5659.         # it to expand with the window.
  5660.         #
  5661.         # Adjust its 'expand' packing state depending on whether the
  5662.         # widget is expanded.
  5663.  
  5664.         parent = widget.get_parent ()
  5665.         (expand, fill,
  5666.          padding, pack_type) = parent.query_child_packing (widget)
  5667.         expand = widget.get_expanded ()
  5668.         parent.set_child_packing (widget, expand, fill,
  5669.                                   padding, pack_type)
  5670.  
  5671.     def device_row_separator_fn (self, model, iter):
  5672.         return model.get_value (iter, 2)
  5673.  
  5674.     def device_row_activated (self, view, path, column):
  5675.         if view.row_expanded (path):
  5676.             view.collapse_row (path)
  5677.         else:
  5678.             view.expand_row (path, False)
  5679.  
  5680.     def device_select_function (self, path):
  5681.         """
  5682.         Allow this path to be selected as long as there
  5683.         is a device associated with it.  Otherwise, expand or collapse it.
  5684.         """
  5685.         model = self.tvNPDevices.get_model ()
  5686.         iter = model.get_iter (path)
  5687.         if model.get_value (iter, 1) != None:
  5688.             return True
  5689.  
  5690.         self.device_row_activated (self.tvNPDevices, path, None)
  5691.         return False
  5692.  
  5693.     def on_tvNPDevices_cursor_changed(self, widget):
  5694.         self.device_selected += 1
  5695.         path, column = widget.get_cursor ()
  5696.         if path == None:
  5697.             return
  5698.  
  5699.         model = widget.get_model ()
  5700.         iter = model.get_iter (path)
  5701.         physicaldevice = model.get_value (iter, 1)
  5702.         if physicaldevice == None:
  5703.             return
  5704.         for device in physicaldevice.get_devices ():
  5705.             if device.type == "parallel":
  5706.                 device.menuentry = _("Parallel Port")
  5707.             elif device.type == "serial":
  5708.                 device.menuentry = _("Serial Port")
  5709.             elif device.type == "usb":
  5710.                 device.menuentry = _("USB")
  5711.             elif device.type == "hp":
  5712.                 device.menuentry = _("HP Linux Imaging and Printing (HPLIP)")
  5713.             elif device.type == "hpfax":
  5714.                 device.menuentry = _("Fax") + " - " + \
  5715.                     _("HP Linux Imaging and Printing (HPLIP)")
  5716.             elif device.type == "hal":
  5717.                 device.menuentry = _("Hardware Abstraction Layer (HAL)")
  5718.             elif device.type == "socket":
  5719.                 device.menuentry = _("AppSocket/HP JetDirect")
  5720.             elif device.type == "lpd":
  5721.                 (scheme, rest) = urllib.splittype (device.uri)
  5722.                 (hostport, rest) = urllib.splithost (rest)
  5723.                 (queue, rest) = urllib.splitquery (rest)
  5724.                 if queue != '':
  5725.                     if queue[0] == '/':
  5726.                         queue = queue[1:]
  5727.  
  5728.                     device.menuentry = _("LPD/LPR queue '%s'") % queue
  5729.                 else:
  5730.                     device.menuentry = _("LPD/LPR queue")
  5731.  
  5732.             elif device.type == "smb":
  5733.                 device.menuentry = _("Windows Printer via SAMBA")
  5734.             elif device.type == "ipp":
  5735.                 device.menuentry = _("IPP")
  5736.             elif device.type == "http" or device.type == "https":
  5737.                 device.menuentry = _("HTTP")
  5738.             else:
  5739.                 device.menuentry = device.uri
  5740.  
  5741.         model = gtk.ListStore (str,                    # URI description
  5742.                                gobject.TYPE_PYOBJECT)  # cupshelpers.Device
  5743.         self.tvNPDeviceURIs.set_model (model)
  5744.  
  5745.         # If this is a network device, check whether HPLIP can drive it.
  5746.         if physicaldevice.get_data ('checked-hplip') != True:
  5747.             hp_drivable = False
  5748.             is_network = False
  5749.             device_dict = { 'device-class': 'network' }
  5750.             for device in physicaldevice.get_devices ():
  5751.                 if device.type == "hp":
  5752.                     # We already know that HPLIP can drive this device.
  5753.                     hp_drivable = True
  5754.                     break
  5755.                 elif device.type in ["socket", "lpd", "ipp"]:
  5756.                     # This is a network printer.
  5757.                     (scheme, rest) = urllib.splittype (device.uri)
  5758.                     (hostport, rest) = urllib.splithost (rest)
  5759.                     if hostport != None:
  5760.                         (host, port) = urllib.splitport (hostport)
  5761.                         is_network = True
  5762.                         self.getNetworkPrinterMakeModel(host=host,
  5763.                                                         device=device)
  5764.                         device_dict['device-info'] = device.info
  5765.                         device_dict['device-make-and-model'] = (device.
  5766.                                                                 make_and_model)
  5767.                         device_dict['device-id'] = device.id
  5768.                         device_dict['device-location'] = device.location
  5769.  
  5770.             if not hp_drivable and is_network:
  5771.                 hplipuri = self.get_hplip_uri_for_network_printer (host,
  5772.                                                                    "print")
  5773.                 if hplipuri:
  5774.                     dev = cupshelpers.Device (hplipuri, **device_dict)
  5775.                     dev.menuentry = "HP Linux Imaging and Printing (HPLIP)"
  5776.                     physicaldevice.add_device (dev)
  5777.  
  5778.                     # Now check to see if we can also send faxes using
  5779.                     # this device.
  5780.                     faxuri = self.get_hplip_uri_for_network_printer (host,
  5781.                                                                      "fax")
  5782.                     if faxuri:
  5783.                         faxdevid = self.get_hpfax_device_id (faxuri)
  5784.                         device_dict['device-id'] = faxdevid
  5785.                         device_dict['device-info'] = _("Fax")
  5786.                         faxdev = cupshelpers.Device (faxuri, **device_dict)
  5787.                         faxdev.menuentry = _("Fax") + " - " + \
  5788.                             "HP Linux Imaging and Printing (HPLIP)"
  5789.                         physicaldevice.add_device (faxdev)
  5790.  
  5791.                 physicaldevice.set_data ('checked-hplip', True)
  5792.  
  5793.         # Fill the list of connections for this device.
  5794.         n = 0
  5795.         for device in physicaldevice.get_devices ():
  5796.             model.append ((device.menuentry, device))
  5797.             n += 1
  5798.         column = self.tvNPDeviceURIs.get_column (0)
  5799.         self.tvNPDeviceURIs.set_cursor (0, column)
  5800.         if n > 1:
  5801.             self.expNPDeviceURIs.show_all ()
  5802.         else:
  5803.             self.expNPDeviceURIs.hide ()
  5804.  
  5805.     def on_tvNPDeviceURIs_cursor_changed(self, widget):
  5806.         path, column = widget.get_cursor ()
  5807.         if path == None:
  5808.             return
  5809.  
  5810.         model = widget.get_model ()
  5811.         iter = model.get_iter (path)
  5812.         device = model.get_value(iter, 1)
  5813.         self.device = device
  5814.         self.lblNPDeviceDescription.set_text ('')
  5815.         page = self.new_printer_device_tabs.get(device.type, 1)
  5816.         self.ntbkNPType.set_current_page(page)
  5817.  
  5818.         location = ''
  5819.         type = device.type
  5820.         url = device.uri.split(":", 1)[-1]
  5821.         if page == 0:
  5822.             # This is the "no options" page, with just a label to describe
  5823.             # the selected device.
  5824.             if device.type == "parallel":
  5825.                 text = _("A printer connected to the parallel port.")
  5826.             elif device.type == "usb":
  5827.                 text = _("A printer connected to a USB port.")
  5828.             elif device.type == "hp":
  5829.                 text = _("HPLIP software driving a printer, "
  5830.                          "or the printer function of a multi-function device.")
  5831.             elif device.type == "hpfax":
  5832.                 text = _("HPLIP software driving a fax machine, "
  5833.                          "or the fax function of a multi-function device.")
  5834.             elif device.type == "hal":
  5835.                 text = _("Local printer detected by the "
  5836.                          "Hardware Abstraction Layer (HAL).")
  5837.             else:
  5838.                 text = device.uri
  5839.  
  5840.             self.lblNPDeviceDescription.set_text (text)
  5841.         elif device.type=="socket":
  5842.             (scheme, rest) = urllib.splittype (device.uri)
  5843.             host = ''
  5844.             port = 9100
  5845.             if scheme == "socket":
  5846.                 (hostport, rest) = urllib.splithost (rest)
  5847.                 (host, port) = urllib.splitnport (hostport, defport=port)
  5848.                 debugprint ("socket: host is %s, port is %s" % (host,
  5849.                                                                 repr (port)))
  5850.                 if device.location != '':
  5851.                     location = device.location
  5852.                 else:
  5853.                     location = host
  5854.             self.entNPTDirectJetHostname.set_text (host)
  5855.             self.entNPTDirectJetPort.set_text (str (port))
  5856.         elif device.type=="serial":
  5857.             if not device.is_class:
  5858.                 options = device.uri.split("?")[1]
  5859.                 options = options.split("+")
  5860.                 option_dict = {}
  5861.                 for option in options:
  5862.                     name, value = option.split("=")
  5863.                     option_dict[name] = value
  5864.  
  5865.                 for widget, name, optionvalues in (
  5866.                     (self.cmbNPTSerialBaud, "baud", None),
  5867.                     (self.cmbNPTSerialBits, "bits", None),
  5868.                     (self.cmbNPTSerialParity, "parity",
  5869.                      ["none", "odd", "even"]),
  5870.                     (self.cmbNPTSerialFlow, "flow",
  5871.                      ["none", "soft", "hard", "hard"])):
  5872.                     if option_dict.has_key(name): # option given in URI?
  5873.                         if optionvalues is None: # use text in widget
  5874.                             model = widget.get_model()
  5875.                             iter = model.get_iter_first()
  5876.                             nr = 0
  5877.                             while iter:
  5878.                                 value = model.get(iter,0)[0]
  5879.                                 if value == option_dict[name]:
  5880.                                     widget.set_active(nr)
  5881.                                     break
  5882.                                 iter = model.iter_next(iter)
  5883.                                 nr += 1
  5884.  
  5885.                             if not iter:
  5886.                                 widget.set_active (0)
  5887.                         else: # use optionvalues
  5888.                             nr = optionvalues.index(
  5889.                                 option_dict[name])
  5890.                             widget.set_active(nr+1) # compensate "Default"
  5891.                     else:
  5892.                         widget.set_active(0)
  5893.  
  5894.         # XXX FILL TABS FOR VALID DEVICE URIs
  5895.         elif device.type in ("ipp", "http", "https"):
  5896.             if device.uri.find (":") != -1:
  5897.                 match = re.match ("(ipp|https?)://([^/]+)(.*)", device.uri)
  5898.                 if match:
  5899.                     server = match.group (2)
  5900.                     printer = match.group (3)
  5901.                 else:
  5902.                     server = ""
  5903.                     printer = ""
  5904.  
  5905.                 self.entNPTIPPHostname.set_text(server)
  5906.                 self.entNPTIPPQueuename.set_text(printer)
  5907.                 self.lblIPPURI.set_text(device.uri)
  5908.                 self.lblIPPURI.show()
  5909.                 self.entNPTIPPQueuename.show()
  5910.                 location = device.location
  5911.             else:
  5912.                 self.entNPTIPPHostname.set_text('')
  5913.                 self.entNPTIPPQueuename.set_text('/printers/')
  5914.                 self.entNPTIPPQueuename.show()
  5915.                 self.lblIPPURI.hide()
  5916.         elif device.type=="lpd":
  5917.             self.cmbentNPTLpdHost.child.set_text ('')
  5918.             self.cmbentNPTLpdQueue.child.set_text ('')
  5919.             model = gtk.ListStore (gobject.TYPE_STRING)
  5920.             self.cmbentNPTLpdQueue.set_model(model)
  5921.             self.btnNPTLpdProbe.set_sensitive (False)
  5922.             if len (device.uri) > 6:
  5923.                 host = device.uri[6:]
  5924.                 i = host.find ("/")
  5925.                 if i != -1:
  5926.                     printer = host[i + 1:]
  5927.                     host = host[:i]
  5928.                 else:
  5929.                     printer = ""
  5930.                 self.cmbentNPTLpdHost.child.set_text (host)
  5931.                 self.cmbentNPTLpdQueue.child.set_text (printer)
  5932.                 location = host
  5933.                 self.btnNPTLpdProbe.set_sensitive (True)
  5934.         elif device.uri == "smb":
  5935.             self.entSMBURI.set_text('')
  5936.             self.btnSMBVerify.set_sensitive(False)
  5937.         elif device.type == "smb":
  5938.             self.entSMBUsername.set_text ('')
  5939.             self.entSMBPassword.set_text ('')
  5940.             self.entSMBURI.set_text(device.uri[6:])
  5941.             self.btnSMBVerify.set_sensitive(True)
  5942.         else:
  5943.             self.entNPTDevice.set_text(device.uri)
  5944.  
  5945.         try:
  5946.             if len (location) == 0 and self.device.device_class == "direct":
  5947.                 # Set location to the name of this host.
  5948.                 if (self.mainapp.connect_server == 'localhost' or
  5949.                     self.mainapp.connect_server[0] == '/'):
  5950.                     u = os.uname ()
  5951.                     location = u[1]
  5952.                 else:
  5953.                     location = self.mainapp.connect_server
  5954.  
  5955.             # Pre-fill location field.
  5956.             self.entNPLocation.set_text (location)
  5957.         except:
  5958.             nonfatalException ()
  5959.  
  5960.         self.setNPButtons()
  5961.  
  5962.     def on_cmbentNPTLpdHost_changed(self, cmbent):
  5963.         hostname = cmbent.get_active_text()
  5964.         self.btnNPTLpdProbe.set_sensitive (len (hostname) > 0)
  5965.         self.setNPButtons()
  5966.  
  5967.     def on_btnNPTLpdProbe_clicked(self, button):
  5968.         # read hostname, probe, fill printer names
  5969.         hostname = self.cmbentNPTLpdHost.get_active_text()
  5970.         server = probe_printer.LpdServer(hostname)
  5971.  
  5972.         self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  5973.                                  _('Searching') + '</span>\n\n' +
  5974.                                  _('Searching for printers'))
  5975.         self.WaitWindow.set_transient_for (self.NewPrinterWindow)
  5976.         self.WaitWindow.show_now ()
  5977.         self.busy (self.WaitWindow)
  5978.         printers = server.probe()
  5979.         self.WaitWindow.hide ()
  5980.  
  5981.         model = gtk.ListStore (gobject.TYPE_STRING)
  5982.         self.cmbentNPTLpdQueue.set_model (model)
  5983.         for printer in printers:
  5984.             self.cmbentNPTLpdQueue.append_text(printer)
  5985.         if printers:
  5986.             self.cmbentNPTLpdQueue.set_active(0)
  5987.  
  5988.     ### Find Network Printer
  5989.     def on_entNPTNetworkHostname_changed(self, ent):
  5990.         s = ent.get_text ()
  5991.         self.btnNetworkFind.set_sensitive (len (s) > 0)
  5992.         self.lblNetworkFindNotFound.hide ()
  5993.         self.setNPButtons ()
  5994.  
  5995.     def on_btnNetworkFind_clicked(self, button):
  5996.         host = self.entNPTNetworkHostname.get_text ()
  5997.  
  5998.         def found_callback (new_device):
  5999.             if self.printer_finder == None:
  6000.                 return
  6001.  
  6002.             glib.idle_add (self.found_network_printer_callback, new_device)
  6003.  
  6004.         self.btnNetworkFind.set_sensitive (False)
  6005.         self.entNPTNetworkHostname.set_sensitive (False)
  6006.         self.network_found = 0
  6007.         self.lblNetworkFindNotFound.hide ()
  6008.         self.lblNetworkFindSearching.show_all ()
  6009.         finder = probe_printer.PrinterFinder ()
  6010.         self.inc_spinner_task ()
  6011.         finder.find (host, found_callback)
  6012.         self.printer_finder = finder
  6013.  
  6014.     def found_network_printer_callback (self, new_device):
  6015.         gtk.gdk.threads_enter ()
  6016.         if new_device:
  6017.             self.network_found += 1
  6018.             dev = PhysicalDevice (new_device)
  6019.             try:
  6020.                 i = self.devices.index (dev)
  6021.  
  6022.                 # Adding a new URI to an existing physical device.
  6023.                 self.devices[i].add_device (new_device)
  6024.  
  6025.                 (path, column) = self.tvNPDevices.get_cursor ()
  6026.                 if path:
  6027.                     model = self.tvNPDevices.get_model ()
  6028.                     iter = model.get_iter (path)
  6029.                     if model.get_value (iter, 1) == self.devices[i]:
  6030.                         self.on_tvNPDevices_cursor_changed (self.tvNPDevices)
  6031.             except ValueError:
  6032.                 # New physical device.
  6033.                 dev.set_data ('checked-hplip', True)
  6034.                 self.devices.append (dev)
  6035.                 self.devices.sort ()
  6036.                 model = self.tvNPDevices.get_model ()
  6037.                 iter = model.insert_before (None, self.devices_find_nw_iter,
  6038.                                             row=[dev.get_info (), dev, False])
  6039.  
  6040.                 # If this is the first one we've found, select it.
  6041.                 if self.network_found == 1:
  6042.                     path = model.get_path (iter)
  6043.                     self.tvNPDevices.set_cursor (path)
  6044.         else:
  6045.             self.printer_finder = None
  6046.             self.dec_spinner_task ()
  6047.             self.lblNetworkFindSearching.hide ()
  6048.             self.entNPTNetworkHostname.set_sensitive (True)
  6049.             self.btnNetworkFind.set_sensitive (True)
  6050.             if self.network_found == 0:
  6051.                 self.lblNetworkFindNotFound.set_markup ('<i>' +
  6052.                                                         _("No printer was "
  6053.                                                           "found at that "
  6054.                                                           "address.") + '</i>')
  6055.                 self.lblNetworkFindNotFound.show ()
  6056.  
  6057.         gtk.gdk.threads_leave ()
  6058.     ###
  6059.  
  6060.     def getDeviceURI(self):
  6061.         type = self.device.type
  6062.         page = self.new_printer_device_tabs.get (type, 1)
  6063.         device = type
  6064.         if page == 0:
  6065.             # The "no options page".  We already have the URI.
  6066.             device = self.device.uri
  6067.         elif type == "socket": # DirectJet
  6068.             host = self.entNPTDirectJetHostname.get_text()
  6069.             port = self.entNPTDirectJetPort.get_text()
  6070.             if host:
  6071.                 device += "://" + host
  6072.                 if port:
  6073.                     device += ":" + port
  6074.         elif type in ("ipp", "http", "https"): # IPP
  6075.             if self.lblIPPURI.get_property('visible'):
  6076.                 device = self.lblIPPURI.get_text()
  6077.         elif type == "lpd": # LPD
  6078.             host = self.cmbentNPTLpdHost.get_active_text()
  6079.             printer = self.cmbentNPTLpdQueue.get_active_text()
  6080.             if host:
  6081.                 device += "://" + host
  6082.                 if printer:
  6083.                     device += "/" + printer
  6084.         elif type == "serial": # Serial
  6085.             options = []
  6086.             for widget, name, optionvalues in (
  6087.                 (self.cmbNPTSerialBaud, "baud", None),
  6088.                 (self.cmbNPTSerialBits, "bits", None),
  6089.                 (self.cmbNPTSerialParity, "parity",
  6090.                  ("none", "odd", "even")),
  6091.                 (self.cmbNPTSerialFlow, "flow",
  6092.                  ("none", "soft", "hard", "hard"))):
  6093.                 nr = widget.get_active()
  6094.                 if nr:
  6095.                     if optionvalues is not None:
  6096.                         option = optionvalues[nr-1]
  6097.                     else:
  6098.                         option = widget.get_active_text()
  6099.                     options.append(name + "=" + option)
  6100.             options = "+".join(options)
  6101.             device =  self.device.uri.split("?")[0] #"serial:/dev/ttyS%s"
  6102.             if options:
  6103.                 device = device + "?" + options
  6104.         elif type == "smb":
  6105.             uri = self.entSMBURI.get_text ()
  6106.             (group, host, share, u, p) = SMBURI (uri=uri).separate ()
  6107.             user = ''
  6108.             password = ''
  6109.             if self.rbtnSMBAuthSet.get_active ():
  6110.                 user = self.entSMBUsername.get_text ()
  6111.                 password = self.entSMBPassword.get_text ()
  6112.             uri = SMBURI (group=group, host=host, share=share,
  6113.                           user=user, password=password).get_uri ()
  6114.             if uri:
  6115.                 device += "://" + uri
  6116.         else:
  6117.             device = self.entNPTDevice.get_text()
  6118.         return device
  6119.  
  6120.     # PPD
  6121.  
  6122.     def on_rbtnNPFoomatic_toggled(self, widget):
  6123.         rbtn1 = self.rbtnNPFoomatic.get_active()
  6124.         rbtn2 = self.rbtnNPPPD.get_active()
  6125.         rbtn3 = self.rbtnNPDownloadableDriverSearch.get_active()
  6126.         self.tvNPMakes.set_sensitive(rbtn1)
  6127.         self.filechooserPPD.set_sensitive(rbtn2)
  6128.  
  6129.         if rbtn1:
  6130.             page = 0
  6131.         if rbtn2:
  6132.             page = 1
  6133.         if rbtn3:
  6134.             page = 2
  6135.         self.ntbkPPDSource.set_current_page (page)
  6136.  
  6137.         if not rbtn3 and self.openprinting_query_handle:
  6138.             # Need to cancel a search in progress.
  6139.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  6140.             self.openprinting_query_handle = None
  6141.             self.btnNPDownloadableDriverSearch_label.set_text (_("Search"))
  6142.             # Clear printer list.
  6143.             model = gtk.ListStore (str, str)
  6144.             combobox = self.cmbNPDownloadableDriverFoundPrinters
  6145.             combobox.set_model (model)
  6146.             combobox.set_sensitive (False)
  6147.  
  6148.         for widget in [self.entNPDownloadableDriverSearch,
  6149.                        self.cmbNPDownloadableDriverFoundPrinters]:
  6150.             widget.set_sensitive(rbtn3)
  6151.         self.btnNPDownloadableDriverSearch.\
  6152.             set_sensitive (rbtn3 and (self.openprinting_query_handle == None))
  6153.  
  6154.         self.setNPButtons()
  6155.  
  6156.     def on_filechooserPPD_selection_changed(self, widget):
  6157.         self.setNPButtons()
  6158.  
  6159.     def on_btnNPDownloadableDriverSearch_clicked(self, widget):
  6160.         if self.openprinting_query_handle != None:
  6161.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  6162.             self.openprinting_query_handle = None
  6163.  
  6164.         widget.set_sensitive (False)
  6165.         label = self.btnNPDownloadableDriverSearch_label
  6166.         label.set_text (_("Searching"))
  6167.         searchterm = self.entNPDownloadableDriverSearch.get_text ()
  6168.         self.openprinting_query_handle = \
  6169.             self.openprinting.searchPrinters (searchterm,
  6170.                                               self.openprinting_printers_found)
  6171.         self.cmbNPDownloadableDriverFoundPrinters.set_sensitive (False)
  6172.  
  6173.     def openprinting_printers_found (self, status, user_data, printers):
  6174.         self.openprinting_query_handle = None
  6175.         button = self.btnNPDownloadableDriverSearch
  6176.         label = self.btnNPDownloadableDriverSearch_label
  6177.         gtk.gdk.threads_enter ()
  6178.         try:
  6179.             label.set_text (_("Search"))
  6180.             button.set_sensitive (True)
  6181.             if status != 0:
  6182.                 # Should report error.
  6183.                 print printers
  6184.                 print traceback.extract_tb(printers[2], limit=None)
  6185.                 gtk.gdk.threads_leave ()
  6186.                 return
  6187.  
  6188.             model = gtk.ListStore (str, str)
  6189.             if len (printers) != 1:
  6190.                 if len (printers) > 1:
  6191.                     first = _("-- Select from search results --")
  6192.                 else:
  6193.                     first = _("-- No matches found --")
  6194.  
  6195.                 iter = model.append (None)
  6196.                 model.set_value (iter, 0, first)
  6197.                 model.set_value (iter, 1, None)
  6198.  
  6199.             sorted_list = []
  6200.             for id, name in printers.iteritems ():
  6201.                 sorted_list.append ((id, name))
  6202.  
  6203.             sorted_list.sort (lambda x, y: cups.modelSort (x[1], y[1]))
  6204.             sought = self.entNPDownloadableDriverSearch.get_text ().lower ()
  6205.             select_index = 0
  6206.             for id, name in sorted_list:
  6207.                 iter = model.append (None)
  6208.                 model.set_value (iter, 0, name)
  6209.                 model.set_value (iter, 1, id)
  6210.                 if name.lower () == sought:
  6211.                     select_index = model.get_path (iter)[0]
  6212.             combobox = self.cmbNPDownloadableDriverFoundPrinters
  6213.             combobox.set_model (model)
  6214.             combobox.set_active (select_index)
  6215.             combobox.set_sensitive (True)
  6216.             self.setNPButtons ()
  6217.         except:
  6218.             nonfatalException()
  6219.         gtk.gdk.threads_leave ()
  6220.  
  6221.     def on_cmbNPDownloadableDriverFoundPrinters_changed(self, widget):
  6222.         self.setNPButtons ()
  6223.  
  6224.         if self.openprinting_query_handle != None:
  6225.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  6226.             self.openprinting_query_handle = None
  6227.             self.drivers_lock.release()
  6228.  
  6229.         model = widget.get_model ()
  6230.         iter = widget.get_active_iter ()
  6231.         if iter:
  6232.             id = model.get_value (iter, 1)
  6233.         else:
  6234.             id = None
  6235.  
  6236.         if id == None:
  6237.             return
  6238.  
  6239.         # A model has been selected, so start the query to find out
  6240.         # which drivers are available.
  6241.         debugprint ("Query drivers for %s" % id)
  6242.         self.drivers_lock.acquire(0)
  6243.         extra_options = dict()
  6244.         if self.DOWNLOADABLE_ONLYPPD:
  6245.             extra_options['onlyppdfiles'] = '1'
  6246.         self.openprinting_query_handle = \
  6247.             self.openprinting.listDrivers (id,
  6248.                                            self.openprinting_drivers_found,
  6249.                                            extra_options=extra_options)
  6250.  
  6251.     def openprinting_drivers_found (self, status, user_data, drivers):
  6252.         if status != 0:
  6253.             # Should report error.
  6254.             print drivers
  6255.             print traceback.extract_tb(drivers[2], limit=None)
  6256.             self.downloadable_drivers = dict()
  6257.             return
  6258.  
  6259.         self.openprinting_query_handle = None
  6260.         self.downloadable_drivers = drivers
  6261.         debugprint ("Drivers query completed: %s" % drivers.keys ())
  6262.         self.drivers_lock.release()
  6263.  
  6264.     def fillDownloadableDrivers(self):
  6265.         # Clear out the properties.
  6266.         self.lblNPDownloadableDriverSupplier.set_text ('')
  6267.         self.lblNPDownloadableDriverLicense.set_text ('')
  6268.         self.lblNPDownloadableDriverDescription.set_text ('')
  6269.         self.lblNPDownloadableDriverSupportContacts.set_text ('')
  6270.         self.rbtnNPDownloadLicenseNo.set_active (True)
  6271.         self.frmNPDownloadableDriverLicenseTerms.hide ()
  6272.  
  6273.         drivers = self.downloadable_drivers
  6274.         model = gtk.ListStore (str,                     # driver name
  6275.                                gobject.TYPE_PYOBJECT)   # driver data
  6276.         recommended_iter = None
  6277.         first_iter = None
  6278.         for driver in drivers.values ():
  6279.             iter = model.append (None)
  6280.             if first_iter == None:
  6281.                 first_iter = iter
  6282.  
  6283.             model.set_value (iter, 0, driver['name'])
  6284.             model.set_value (iter, 1, driver)
  6285.  
  6286.             if driver['recommended']:
  6287.                 recommended_iter = iter
  6288.  
  6289.         if recommended_iter == None:
  6290.             recommended_iter = first_iter
  6291.  
  6292.         treeview = self.tvNPDownloadableDrivers
  6293.         treeview.set_model (model)
  6294.         if recommended_iter != None:
  6295.             treeview.get_selection ().select_iter (recommended_iter)
  6296.  
  6297.     def on_rbtnNPDownloadLicense_toggled(self, widget):
  6298.         self.setNPButtons ()
  6299.  
  6300.     # PPD from foomatic
  6301.  
  6302.     def fillMakeList(self):
  6303.         makes = self.ppds.getMakes()
  6304.         model = self.tvNPMakes.get_model()
  6305.         model.clear()
  6306.         found = False
  6307.         if self.auto_make:
  6308.             auto_make_lower = self.auto_make.lower ()
  6309.         else:
  6310.             auto_make_lower = None
  6311.  
  6312.         for make in makes:
  6313.             iter = model.append((make,))
  6314.             if auto_make_lower != None and make.lower() == auto_make_lower:
  6315.                 path = model.get_path(iter)
  6316.                 self.tvNPMakes.set_cursor (path)
  6317.                 self.tvNPMakes.scroll_to_cell(path, None,
  6318.                                               True, 0.5, 0.5)
  6319.                 found = True
  6320.  
  6321.         if not found:
  6322.             self.tvNPMakes.set_cursor (0,)
  6323.             self.tvNPMakes.scroll_to_cell(0, None, True, 0.0, 0.0)
  6324.  
  6325.         # Also pre-fill the OpenPrinting.org search box.
  6326.         search = ''
  6327.         if self.device and self.device.id_dict:
  6328.             devid_dict = self.device.id_dict
  6329.             if devid_dict["MFG"] and devid_dict["MDL"]:
  6330.                 search = devid_dict["MFG"] + " " + devid_dict["MDL"]
  6331.             elif devid_dict["DES"]:
  6332.                 search = devid_dict["DES"]
  6333.             elif devid_dict["MFG"]:
  6334.                 search = devid_dict["MFG"]
  6335.         if search == '' and self.auto_make != None:
  6336.             search += self.auto_make
  6337.             if self.auto_model != None:
  6338.                 search += " " + self.auto_model
  6339.         self.entNPDownloadableDriverSearch.set_text (search)
  6340.  
  6341.     def on_tvNPMakes_cursor_changed(self, tvNPMakes):
  6342.         path, column = tvNPMakes.get_cursor()
  6343.         if path != None:
  6344.             model = tvNPMakes.get_model ()
  6345.             iter = model.get_iter (path)
  6346.             self.NPMake = model.get(iter, 0)[0]
  6347.             self.fillModelList()
  6348.  
  6349.     def fillModelList(self):
  6350.         models = self.ppds.getModels(self.NPMake)
  6351.         model = self.tvNPModels.get_model()
  6352.         model.clear()
  6353.         selected = False
  6354.         is_auto_make = self.NPMake.lower () == self.auto_make.lower ()
  6355.         if is_auto_make:
  6356.             auto_model_lower = self.auto_model.lower ()
  6357.  
  6358.         for pmodel in models:
  6359.             iter = model.append((pmodel,))
  6360.             if is_auto_make and pmodel.lower() == auto_model_lower:
  6361.                 path = model.get_path(iter)
  6362.                 self.tvNPModels.set_cursor (path)
  6363.                 self.tvNPModels.scroll_to_cell(path, None,
  6364.                                                True, 0.5, 0.5)
  6365.                 selected = True
  6366.         if not selected:
  6367.             self.tvNPModels.set_cursor (0,)
  6368.             self.tvNPModels.scroll_to_cell(0, None, True, 0.0, 0.0)
  6369.         self.tvNPModels.columns_autosize()
  6370.  
  6371.     def fillDriverList(self, pmake, pmodel):
  6372.         self.NPModel = pmodel
  6373.         model = self.tvNPDrivers.get_model()
  6374.         model.clear()
  6375.  
  6376.         ppds = self.ppds.getInfoFromModel(pmake, pmodel)
  6377.  
  6378.         self.NPDrivers = self.ppds.orderPPDNamesByPreference(ppds.keys(),
  6379.                                              self.jockey_installed_files)
  6380.         if self.auto_driver and self.device:
  6381.             drivers = []
  6382.             for driver in self.NPDrivers:
  6383.                 if driver == self.auto_driver:
  6384.                     drivers.insert (0, driver)
  6385.                 else:
  6386.                     drivers.append (driver)
  6387.  
  6388.             self.NPDrivers = drivers
  6389.  
  6390.         for i in range (len(self.NPDrivers)):
  6391.             ppd = ppds[self.NPDrivers[i]]
  6392.             driver = ppd["ppd-make-and-model"]
  6393.             driver = driver.replace(" (recommended)", "")
  6394.  
  6395.             try:
  6396.                 lpostfix = " [%s]" % ppd["ppd-natural-language"]
  6397.                 driver += lpostfix
  6398.             except KeyError:
  6399.                 pass
  6400.  
  6401.             if not self.device and self.auto_driver == self.NPDrivers[i]:
  6402.                 iter = model.append ((driver + _(" (Current)"),))
  6403.                 path = model.get_path (iter)
  6404.                 self.tvNPDrivers.get_selection().select_path(path)
  6405.                 self.tvNPDrivers.scroll_to_cell(path, None, True, 0.5, 0.0)
  6406.             elif self.device and i == 0:
  6407.                 iter = model.append ((driver + _(" (recommended)"),))
  6408.                 path = model.get_path (iter)
  6409.                 self.tvNPDrivers.get_selection().select_path(path)
  6410.                 self.tvNPDrivers.scroll_to_cell(path, None, True, 0.5, 0.0)
  6411.             else:
  6412.                 model.append((driver, ))
  6413.         self.tvNPDrivers.columns_autosize()
  6414.  
  6415.     def on_NPDrivers_query_tooltip(self, tv, x, y, keyboard_mode, tooltip):
  6416.         if keyboard_mode:
  6417.             path = tv.get_cursor()[0]
  6418.             if path is None:
  6419.                 return False
  6420.         else:
  6421.             bin_x, bin_y = tv.convert_widget_to_bin_window_coords(x, y)
  6422.             ret = tv.get_path_at_pos (bin_x, bin_y)
  6423.             if ret is None:
  6424.                 return False
  6425.             path = ret[0]
  6426.  
  6427.         drivername = self.NPDrivers[path[0]]
  6428.         ppddict = self.ppds.getInfoFromPPDName(drivername)
  6429.         markup = ppddict['ppd-make-and-model']
  6430.         if (drivername.startswith ("foomatic:")):
  6431.             markup += " "
  6432.             markup += _("This PPD is generated by foomatic.")
  6433.         tooltip.set_markup(markup)
  6434.         return True
  6435.  
  6436.     def on_tvNPModels_cursor_changed(self, widget):
  6437.         path, column = widget.get_cursor()
  6438.         if path != None:
  6439.             model = widget.get_model ()
  6440.             iter = model.get_iter (path)
  6441.             pmodel = model.get(iter, 0)[0]
  6442.             self.fillDriverList(self.NPMake, pmodel)
  6443.             self.on_tvNPDrivers_cursor_changed(self.tvNPDrivers)
  6444.  
  6445.     def on_tvNPDrivers_cursor_changed(self, widget):
  6446.         self.setNPButtons()
  6447.  
  6448.     def on_tvNPDownloadableDrivers_cursor_changed(self, widget):
  6449.         model, iter = widget.get_selection ().get_selected ()
  6450.         if not iter:
  6451.             path, column = widget.get_cursor()
  6452.             iter = model.get_iter (path)
  6453.         driver = model.get_value (iter, 1)
  6454.         import pprint
  6455.         pprint.pprint (driver)
  6456.         self.ntbkNPDownloadableDriverProperties.set_current_page(1)
  6457.         supplier = driver.get('supplier', _("OpenPrinting"))
  6458.         vendor = self.cbNPDownloadableDriverSupplierVendor
  6459.         active = driver['manufacturersupplied']
  6460.  
  6461.         def set_protect_active (widget, active):
  6462.             widget.set_active (active)
  6463.             widget.set_data ('protect_active', active)
  6464.  
  6465.         set_protect_active (vendor, active)
  6466.         self.lblNPDownloadableDriverSupplier.set_text (supplier)
  6467.  
  6468.         license = driver.get('license', _("Distributable"))
  6469.         patents = self.cbNPDownloadableDriverLicensePatents
  6470.         free = self.cbNPDownloadableDriverLicenseFree
  6471.         set_protect_active (patents, driver['patents'])
  6472.         set_protect_active (free, driver['freesoftware'])
  6473.         self.lblNPDownloadableDriverLicense.set_text (license)
  6474.  
  6475.         description = driver.get('shortdescription', _("None"))
  6476.         self.lblNPDownloadableDriverDescription.set_markup (description)
  6477.  
  6478.         functionality = driver['functionality']
  6479.         for field in ["Graphics", "LineArt", "Photo", "Text"]:
  6480.             key = field.lower ()
  6481.             value = None
  6482.             hs = self.__dict__.get ("hsDownloadableDriverPerf%s" % field)
  6483.             unknown = self.__dict__.get ("lblDownloadableDriverPerf%sUnknown"
  6484.                                          % field)
  6485.             if functionality.has_key (key):
  6486.                 if hs:
  6487.                     try:
  6488.                         value = int (functionality[key])
  6489.                         hs.set_value (value)
  6490.                         hs.show_all ()
  6491.                         unknown.hide ()
  6492.                     except:
  6493.                         pass
  6494.  
  6495.             if value == None:
  6496.                 hs.hide ()
  6497.                 unknown.show_all ()
  6498.         supportcontacts = ""
  6499.         if driver.has_key ('supportcontacts'):
  6500.             for supportentry in driver['supportcontacts']:
  6501.                 if supportentry['name']:
  6502.                     supportcontact = " - " + supportentry['name']
  6503.                     supportcontact_extra = ""
  6504.                     if supportentry['url']:
  6505.                         supportcontact_extra = supportcontact_extra + \
  6506.                             supportentry['url']
  6507.                     if supportentry['level']:
  6508.                         if supportcontact_extra:
  6509.                             supportcontact_extra = supportcontact_extra + _(", ")
  6510.                         supportcontact_extra = supportcontact_extra + \
  6511.                             supportentry['level']
  6512.                     if supportcontact_extra:
  6513.                         supportcontact = supportcontact + \
  6514.                             _("\n(%s)") % supportcontact_extra
  6515.                         if supportcontacts:
  6516.                             supportcontacts = supportcontacts + "\n"
  6517.                         supportcontacts = supportcontacts + supportcontact
  6518.         if not supportcontacts:
  6519.             supportcontacts = _("No support contacts known")
  6520.         self.lblNPDownloadableDriverSupportContacts.set_text (supportcontacts)
  6521.         if driver.has_key ('licensetext'):
  6522.             self.frmNPDownloadableDriverLicenseTerms.show ()
  6523.             terms = driver.get('licensetext', _("Not specified."))
  6524.             self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms)
  6525.         else:
  6526.             self.frmNPDownloadableDriverLicenseTerms.hide ()
  6527.         if not driver['nonfreesoftware'] and not driver['patents']:
  6528.             self.rbtnNPDownloadLicenseYes.set_active (True)
  6529.             self.rbtnNPDownloadLicenseYes.hide ()
  6530.             self.rbtnNPDownloadLicenseNo.hide ()
  6531.         else:
  6532.             self.rbtnNPDownloadLicenseNo.set_active (True)
  6533.             self.rbtnNPDownloadLicenseYes.show ()
  6534.             self.rbtnNPDownloadLicenseNo.show ()
  6535.             self.frmNPDownloadableDriverLicenseTerms.show ()
  6536.             terms = driver.get('licensetext', _("Not specified."))
  6537.             self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms)
  6538.  
  6539.         self.setNPButtons()
  6540.  
  6541.     def getNPPPD(self):
  6542.         try:
  6543.             if self.rbtnNPFoomatic.get_active():
  6544.                 model, iter = self.tvNPDrivers.get_selection().get_selected()
  6545.                 nr = model.get_path(iter)[0]
  6546.                 ppd = self.NPDrivers[nr]
  6547.             elif self.rbtnNPPPD.get_active():
  6548.                 ppd = cups.PPD(self.filechooserPPD.get_filename())
  6549.             else:
  6550.                 # PPD of the driver downloaded from OpenPrinting XXX
  6551.                 treeview = self.tvNPDownloadableDrivers
  6552.                 model, iter = treeview.get_selection ().get_selected ()
  6553.                 driver = model.get_value (iter, 1)
  6554.                 if driver.has_key ('ppds'):
  6555.                     # Only need to download a PPD.
  6556.                     if (len(driver['ppds']) > 0):
  6557.                         file_to_download = driver['ppds'][0]
  6558.                         debugprint ("ppd file to download [" + file_to_download+ "]")
  6559.                         file_to_download = file_to_download.strip()
  6560.                         if (len(file_to_download) > 0):
  6561.                             ppdurlobj = urllib.urlopen(file_to_download)
  6562.                             ppdcontent = ppdurlobj.read()
  6563.                             ppdurlobj.close()
  6564.                             (tmpfd, ppdname) = tempfile.mkstemp()
  6565.                             debugprint(ppdname)
  6566.                             ppdfile = os.fdopen(tmpfd, 'w')
  6567.                             ppdfile.write(ppdcontent)
  6568.                             ppdfile.close()
  6569.                             ppd = cups.PPD(ppdname)
  6570.                             os.unlink(ppdname)
  6571.  
  6572.         except RuntimeError, e:
  6573.             debugprint ("RuntimeError: " + str(e))
  6574.             if self.rbtnNPFoomatic.get_active():
  6575.                 # Foomatic database problem of some sort.
  6576.                 err_title = _('Database error')
  6577.                 err_text = _("The '%s' driver cannot be "
  6578.                              "used with printer '%s %s'.")
  6579.                 model, iter = (self.tvNPDrivers.get_selection().
  6580.                                get_selected())
  6581.                 nr = model.get_path(iter)[0]
  6582.                 driver = self.NPDrivers[nr]
  6583.                 if driver.startswith ("gutenprint"):
  6584.                     # This printer references some XML that is not
  6585.                     # installed by default.  Point the user at the
  6586.                     # package they need to install.
  6587.                     err = _("You will need to install the '%s' package "
  6588.                             "in order to use this driver.") % \
  6589.                             "gutenprint-foomatic"
  6590.                 else:
  6591.                     err = err_text % (driver, self.NPMake, self.NPModel)
  6592.             elif self.rbtnNPPPD.get_active():
  6593.                 # This error came from trying to open the PPD file.
  6594.                 err_title = _('PPD error')
  6595.                 filename = self.filechooserPPD.get_filename()
  6596.                 err = _('Failed to read PPD file.  Possible reason '
  6597.                         'follows:') + '\n'
  6598.                 try:
  6599.                     # We want this to be in the current natural language,
  6600.                     # so we intentionally don't set LC_ALL=C here.
  6601.                     p = subprocess.Popen (['/usr/bin/cupstestppd',
  6602.                                            '-rvv', filename],
  6603.                                           close_fds=True,
  6604.                                           stdin=file("/dev/null"),
  6605.                                           stdout=subprocess.PIPE,
  6606.                                           stderr=subprocess.PIPE)
  6607.                     (stdout, stderr) = p.communicate ()
  6608.                     err += stdout
  6609.                 except:
  6610.                     # Problem executing command.
  6611.                     raise
  6612.             else:
  6613.                 # Failed to get PPD downloaded from OpenPrinting XXX
  6614.                 err_title = _('Downloadable drivers')
  6615.                 err = _("Failed to download PPD.")
  6616.  
  6617.             show_error_dialog (err_title, err, self.NewPrinterWindow)
  6618.             return None
  6619.  
  6620.         debugprint("ppd: " + repr(ppd))
  6621.         if isinstance(ppd, str) or isinstance(ppd, unicode):
  6622.             self.mainapp.cups._begin_operation (_("fetching PPD"))
  6623.             try:
  6624.                 if ppd != "raw":
  6625.                     f = self.mainapp.cups.getServerPPD(ppd)
  6626.                     ppd = cups.PPD(f)
  6627.                     os.unlink(f)
  6628.             except RuntimeError:
  6629.                 nonfatalException()
  6630.                 debugprint ("libcups from CUPS 1.3 not available: never mind")
  6631.             except cups.IPPError:
  6632.                 nonfatalException()
  6633.                 debugprint ("CUPS 1.3 server not available: never mind")
  6634.  
  6635.             self.mainapp.cups._end_operation ()
  6636.  
  6637.         return ppd
  6638.  
  6639.     # Installable Options
  6640.  
  6641.     def fillNPInstallableOptions(self):
  6642.         debugprint ("Examining installable options")
  6643.         self.installable_options = False
  6644.         self.options = { }
  6645.  
  6646.         container = self.vbNPInstallOptions
  6647.         for child in container.get_children():
  6648.             container.remove(child)
  6649.  
  6650.         if not self.ppd:
  6651.             l = gtk.Label(_("No Installable Options"))
  6652.             container.add(l)
  6653.             l.show()
  6654.             debugprint ("No PPD so no installable options")
  6655.             return
  6656.  
  6657.         # build option tabs
  6658.         for group in self.ppd.optionGroups:
  6659.             if group.name != "InstallableOptions":
  6660.                 continue
  6661.             self.installable_options = True
  6662.  
  6663.             table = gtk.Table(1, 3, False)
  6664.             table.set_col_spacings(6)
  6665.             table.set_row_spacings(6)
  6666.             container.add(table)
  6667.             rows = 0
  6668.  
  6669.             for nr, option in enumerate(group.options):
  6670.                 if option.keyword == "PageRegion":
  6671.                     continue
  6672.                 rows += 1
  6673.                 table.resize (rows, 3)
  6674.                 o = OptionWidget(option, self.ppd, self)
  6675.                 table.attach(o.conflictIcon, 0, 1, nr, nr+1, 0, 0, 0, 0)
  6676.  
  6677.                 hbox = gtk.HBox()
  6678.                 if o.label:
  6679.                     a = gtk.Alignment (0.5, 0.5, 1.0, 1.0)
  6680.                     a.set_padding (0, 0, 0, 6)
  6681.                     a.add (o.label)
  6682.                     table.attach(a, 1, 2, nr, nr+1, gtk.FILL, 0, 0, 0)
  6683.                     table.attach(hbox, 2, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  6684.                 else:
  6685.                     table.attach(hbox, 1, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  6686.                 hbox.pack_start(o.selector, False)
  6687.                 self.options[option.keyword] = o
  6688.         if not self.installable_options:
  6689.             l = gtk.Label(_("No Installable Options"))
  6690.             container.add(l)
  6691.             l.show()
  6692.         self.scrNPInstallableOptions.hide()
  6693.         self.scrNPInstallableOptions.show_all()
  6694.  
  6695.  
  6696.     # Create new Printer
  6697.     def on_btnNPApply_clicked(self, widget):
  6698.         if self.fetchDevices_conn:
  6699.             self.fetchDevices_conn.destroy ()
  6700.             self.fetchDevices_conn = None
  6701.             self.dec_spinner_task ()
  6702.  
  6703.         if self.ppdsloader:
  6704.             self.ppdsloader.destroy ()
  6705.             self.ppdsloader = None
  6706.  
  6707.         if self.printer_finder:
  6708.             self.printer_finder.cancel ()
  6709.             self.printer_finder = None
  6710.             self.dec_spinner_task ()
  6711.  
  6712.         if self.dialog_mode in ("class", "printer", "printer_with_uri"):
  6713.             name = unicode (self.entNPName.get_text())
  6714.             location = unicode (self.entNPLocation.get_text())
  6715.             info = unicode (self.entNPDescription.get_text())
  6716.         else:
  6717.             if not self.mainapp.printer:
  6718.                 # Printer has disappeared
  6719.                 return
  6720.             else:
  6721.                 name = self.mainapp.printer.name
  6722.  
  6723.         # Whether to check for missing drivers.
  6724.         check = False
  6725.         checkppd = None
  6726.         ppd = self.ppd
  6727.  
  6728.         if self.dialog_mode == "class":
  6729.             members = getCurrentClassMembers(self.tvNCMembers)
  6730.             try:
  6731.                 for member in members:
  6732.                     self.mainapp.cups.addPrinterToClass(member, name)
  6733.             except cups.IPPError, (e, msg):
  6734.                 self.show_IPP_Error(e, msg)
  6735.                 return
  6736.         elif self.dialog_mode == "printer" or \
  6737.                 self.dialog_mode == "printer_with_uri":
  6738.             uri = None
  6739.             if self.device.uri:
  6740.                 uri = self.device.uri
  6741.             else:
  6742.                 uri = self.getDeviceURI()
  6743.             if not self.ppd: # XXX needed?
  6744.                 # Go back to previous page to re-select driver.
  6745.                 self.nextNPTab(-1)
  6746.                 return
  6747.  
  6748.             # write Installable Options to ppd
  6749.             for option in self.options.itervalues():
  6750.                 option.writeback()
  6751.  
  6752.             self.busy (self.NewPrinterWindow)
  6753.             while gtk.events_pending ():
  6754.                 gtk.main_iteration ()
  6755.             self.mainapp.cups._begin_operation (_("adding printer %s") % name)
  6756.             try:
  6757.                 if isinstance(ppd, str) or isinstance(ppd, unicode):
  6758.                     self.mainapp.cups.addPrinter(name, ppdname=ppd,
  6759.                          device=uri, info=info, location=location)
  6760.                     check = True
  6761.                 elif ppd is None: # raw queue
  6762.                     self.mainapp.cups.addPrinter(name, device=uri,
  6763.                                          info=info, location=location)
  6764.                 else:
  6765.                     cupshelpers.setPPDPageSize(ppd, self.language[0])
  6766.                     self.mainapp.cups.addPrinter(name, ppd=ppd,
  6767.                          device=uri, info=info, location=location)
  6768.                     check = True
  6769.                     checkppd = ppd
  6770.             except cups.IPPError, (e, msg):
  6771.                 self.ready (self.NewPrinterWindow)
  6772.                 self.show_IPP_Error(e, msg)
  6773.                 self.mainapp.cups._end_operation()
  6774.                 return
  6775.             except:
  6776.                 self.ready (self.NewPrinterWindow)
  6777.                 self.mainapp.cups._end_operation()
  6778.                 fatalException (1)
  6779.             self.mainapp.cups._end_operation()
  6780.             self.ready (self.NewPrinterWindow)
  6781.         if self.dialog_mode in ("class", "printer", "printer_with_uri"):
  6782.             self.mainapp.cups._begin_operation (_("modifying printer %s") %
  6783.                                                 name)
  6784.             try:
  6785.                 cupshelpers.activateNewPrinter (self.mainapp.cups, name)
  6786.                 self.mainapp.cups.setPrinterLocation(name, location)
  6787.                 self.mainapp.cups.setPrinterInfo(name, info)
  6788.             except cups.IPPError, (e, msg):
  6789.                 self.show_IPP_Error(e, msg)
  6790.                 self.mainapp.cups._end_operation ()
  6791.                 return
  6792.             self.mainapp.cups._end_operation ()
  6793.         elif self.dialog_mode == "device":
  6794.             self.mainapp.cups._begin_operation (_("modifying printer %s") %
  6795.                                                 name)
  6796.             try:
  6797.                 uri = self.getDeviceURI()
  6798.                 self.mainapp.cups.addPrinter(name, device=uri)
  6799.             except cups.IPPError, (e, msg):
  6800.                 self.show_IPP_Error(e, msg)
  6801.                 self.mainapp.cups._end_operation ()
  6802.                 return
  6803.             self.mainapp.cups._end_operation ()
  6804.         elif self.dialog_mode == "ppd":
  6805.             if not ppd:
  6806.                 ppd = self.ppd = self.getNPPPD()
  6807.                 if not ppd:
  6808.                     # Go back to previous page to re-select driver.
  6809.                     self.nextNPTab(-1)
  6810.                     return
  6811.  
  6812.             self.mainapp.cups._begin_operation (_("modifying printer %s") %
  6813.                                                 name)
  6814.             # set ppd on server and retrieve it
  6815.             # cups doesn't offer a way to just download a ppd ;(=
  6816.             raw = False
  6817.             if isinstance(ppd, str) or isinstance(ppd, unicode):
  6818.                 if self.rbtnChangePPDasIs.get_active():
  6819.                     # To use the PPD as-is we need to prevent CUPS copying
  6820.                     # the old options over.  Do this by setting it to a
  6821.                     # raw queue (no PPD) first.
  6822.                     try:
  6823.                         self.mainapp.cups.addPrinter(name, ppdname='raw')
  6824.                     except cups.IPPError, (e, msg):
  6825.                         self.show_IPP_Error(e, msg)
  6826.                 try:
  6827.                     self.mainapp.cups.addPrinter(name, ppdname=ppd)
  6828.                 except cups.IPPError, (e, msg):
  6829.                     self.show_IPP_Error(e, msg)
  6830.                     self.mainapp.cups._end_operation ()
  6831.                     return
  6832.  
  6833.                 try:
  6834.                     filename = self.mainapp.cups.getPPD(name)
  6835.                     ppd = cups.PPD(filename)
  6836.                     os.unlink(filename)
  6837.                 except cups.IPPError, (e, msg):
  6838.                     if e == cups.IPP_NOT_FOUND:
  6839.                         raw = True
  6840.                     else:
  6841.                         self.show_IPP_Error(e, msg)
  6842.                         self.mainapp.cups._end_operation ()
  6843.                         return
  6844.             else:
  6845.                 # We have an actual PPD to upload, not just a name.
  6846.                 if ((not self.rbtnChangePPDasIs.get_active()) and
  6847.                     isinstance (self.mainapp.ppd, cups.PPD)):
  6848.                     cupshelpers.copyPPDOptions(self.mainapp.ppd, ppd)
  6849.                 else:
  6850.                     # write Installable Options to ppd
  6851.                     for option in self.options.itervalues():
  6852.                         option.writeback()
  6853.                     cupshelpers.setPPDPageSize(ppd, self.language[0])
  6854.  
  6855.                 try:
  6856.                     self.mainapp.cups.addPrinter(name, ppd=ppd)
  6857.                 except cups.IPPError, (e, msg):
  6858.                     self.show_IPP_Error(e, msg)
  6859.                     self.mainapp.cups._end_operation ()
  6860.                     return
  6861.  
  6862.             self.mainapp.cups._end_operation ()
  6863.  
  6864.             if not raw:
  6865.                 check = True
  6866.                 checkppd = ppd
  6867.  
  6868.         self.NewPrinterWindow.hide()
  6869.         self.device = None
  6870.         self.mainapp.populateList()
  6871.  
  6872.         # Now select it.
  6873.         dests_iconview = self.mainapp.dests_iconview
  6874.         model = dests_iconview.get_model ()
  6875.         iter = model.get_iter_first ()
  6876.         while iter != None:
  6877.             queue = unicode (model.get_value (iter, 2))
  6878.             if queue == name:
  6879.                 path = model.get_path (iter)
  6880.                 dests_iconview.scroll_to_path (path, True, 0.5, 0.5)
  6881.                 dests_iconview.unselect_all ()
  6882.                 dests_iconview.set_cursor (path)
  6883.                 dests_iconview.select_path (path)
  6884.                 break
  6885.  
  6886.             iter = model.iter_next (iter)
  6887.  
  6888.         if not self.mainapp.printers.has_key (name):
  6889.             # At this stage the printer has disappeared even though we
  6890.             # only added it moments ago.
  6891.             return
  6892.  
  6893.         # Load information about the printer,
  6894.         # e.g. self.mainapp.server_side_options and self.mainapp.ppd
  6895.         # (both used below).
  6896.         self.mainapp.fillPrinterTab (name)
  6897.  
  6898.         # Select 'Settings' in the properties treeview.
  6899.         self.mainapp.tvPrinterProperties.set_cursor ((0,))
  6900.  
  6901.         if check:
  6902.             try:
  6903.                 self.checkDriverExists (name, ppd=checkppd)
  6904.             except:
  6905.                 nonfatalException()
  6906.  
  6907.             # Also check to see whether the media option has become
  6908.             # invalid.  This can happen if it had previously been
  6909.             # explicitly set to a page size that is not offered with
  6910.             # the new PPD (see bug #441836).
  6911.             try:
  6912.                 option = self.mainapp.server_side_options['media']
  6913.                 if option.get_current_value () == None:
  6914.                     debugprint ("Invalid media option: resetting")
  6915.                     option.reset ()
  6916.                     self.mainapp.changed.add (option)
  6917.                     self.mainapp.save_printer (self.mainapp.printer)
  6918.             except KeyError:
  6919.                 pass
  6920.             except:
  6921.                 nonfatalException()
  6922.  
  6923.         # Finally, suggest printing a test page.
  6924.         if (self.dialog_mode == "printer" or \
  6925.             self.dialog_mode == "printer_with_uri") and \
  6926.             self.mainapp.ppd != False:
  6927.             q = gtk.MessageDialog (self.mainapp.PrintersWindow,
  6928.                                    gtk.DIALOG_DESTROY_WITH_PARENT |
  6929.                                    gtk.DIALOG_MODAL,
  6930.                                    gtk.MESSAGE_QUESTION,
  6931.                                    gtk.BUTTONS_NONE,
  6932.                                    _("Would you like to print a test page?"))
  6933.             q.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
  6934.                            _("Print Test Page"), gtk.RESPONSE_YES)
  6935.             response = q.run ()
  6936.             q.destroy ()
  6937.             if response == gtk.RESPONSE_YES:
  6938.                 self.mainapp.PrinterPropertiesDialog.hide ()
  6939.  
  6940.                 properties_shown = False
  6941.                 try:
  6942.                     # Load the printer details but hide the properties dialog.
  6943.                     self.mainapp.display_properties_dialog_for (name)
  6944.                     properties_shown = True
  6945.                 except RuntimeError:
  6946.                     pass
  6947.  
  6948.                 if properties_shown:
  6949.                     # Click the test button.
  6950.                     self.mainapp.btnPrintTestPage.clicked ()
  6951.  
  6952.     def checkDriverExists(self, name, ppd=None):
  6953.         """Check that the driver for an existing queue actually
  6954.         exists, and prompt to install the appropriate package
  6955.         if not.
  6956.  
  6957.         ppd: cups.PPD object, if already created"""
  6958.  
  6959.         # Is this queue on the local machine?  If not, we can't check
  6960.         # anything at all.
  6961.         server = cups.getServer ()
  6962.         if not (server == 'localhost' or server == '127.0.0.1' or
  6963.                 server == '::1' or server[0] == '/'):
  6964.             return
  6965.  
  6966.         # Fetch the PPD if we haven't already.
  6967.         if not ppd:
  6968.             try:
  6969.                 filename = self.mainapp.cups.getPPD(name)
  6970.             except cups.IPPError, (e, msg):
  6971.                 if e == cups.IPP_NOT_FOUND:
  6972.                     # This is a raw queue.  Nothing to check.
  6973.                     return
  6974.                 else:
  6975.                     self.show_IPP_Error(e, msg)
  6976.                     return
  6977.  
  6978.             ppd = cups.PPD(filename)
  6979.             os.unlink(filename)
  6980.  
  6981.         (pkgs, exes) = cupshelpers.missingPackagesAndExecutables (ppd)
  6982.         if len (pkgs) > 0 or len (exes) > 0:
  6983.             # We didn't find a necessary executable.  Complain.
  6984.             can_install = False
  6985.             if len (pkgs) > 0:
  6986.                 try:
  6987.                     pk = installpackage.PackageKit ()
  6988.                     can_install = True
  6989.                 except:
  6990.                     pass
  6991.  
  6992.             if can_install and len (pkgs) > 0:
  6993.                 pkg = pkgs[0]
  6994.                 install_text = ('<span weight="bold" size="larger">' +
  6995.                                 _('Install driver') + '</span>\n\n' +
  6996.                                 _("Printer '%s' requires the %s package but "
  6997.                                   "it is not currently installed.") %
  6998.                                 (name, pkg))
  6999.                 dialog = self.InstallDialog
  7000.                 self.lblInstall.set_markup(install_text)
  7001.                 dialog.set_transient_for (self.parent)
  7002.                 response = dialog.run ()
  7003.                 dialog.hide ()
  7004.                 if response == gtk.RESPONSE_OK:
  7005.                     # Install the package.
  7006.                     try:
  7007.                         xid = self.mainapp.PrintersWindow.window.xid
  7008.                         pk.InstallPackageName (xid, 0, pkg)
  7009.                     except:
  7010.                         pass # should handle error
  7011.             else:
  7012.                 show_error_dialog (_('Missing driver'),
  7013.                                    _("Printer '%s' requires the '%s' program "
  7014.                                      "but it is not currently installed.  "
  7015.                                      "Please install it before using this "
  7016.                                      "printer.") % (name, (exes + pkgs)[0]),
  7017.                                    self.parent)
  7018.  
  7019.  
  7020. def main(setup_printer = None, configure_printer = None, change_ppd = False,
  7021.          devid = "", print_test_page = False, focus_on_map = True):
  7022.     cups.setUser (os.environ.get ("CUPS_USER", cups.getUser()))
  7023.     gobject.threads_init()
  7024.     gtk.gdk.threads_init()
  7025.  
  7026.     mainwindow = GUI(setup_printer, configure_printer, change_ppd, devid,
  7027.                      print_test_page, focus_on_map)
  7028.  
  7029.     if gtk.__dict__.has_key("main"):
  7030.         gtk.main()
  7031.     else:
  7032.         gtk.mainloop()
  7033.  
  7034.  
  7035. if __name__ == "__main__":
  7036.     import getopt
  7037.     try:
  7038.         opts, args = getopt.gnu_getopt (sys.argv[1:], '',
  7039.                                         ['setup-printer=',
  7040.                                          'configure-printer=',
  7041.                                          'choose-driver=',
  7042.                                          'devid=',
  7043.                                          'print-test-page=',
  7044.                                          'no-focus-on-map',
  7045.                                          'debug'])
  7046.     except getopt.GetoptError:
  7047.         show_help ()
  7048.         sys.exit (1)
  7049.  
  7050.     setup_printer = None
  7051.     configure_printer = None
  7052.     change_ppd = False
  7053.     print_test_page = False
  7054.     focus_on_map = True
  7055.     devid = ""
  7056.     for opt, optarg in opts:
  7057.         if (opt == "--configure-printer" or
  7058.             opt == "--choose-driver" or
  7059.             opt == "--print-test-page"):
  7060.             configure_printer = optarg
  7061.             if opt == "--choose-driver":
  7062.                 change_ppd = True
  7063.             elif opt == "--print-test-page":
  7064.                 print_test_page = True
  7065.  
  7066.         elif opt == '--setup-printer':
  7067.             setup_printer = optarg
  7068.  
  7069.         elif opt == '--devid':
  7070.             devid = optarg
  7071.  
  7072.         elif opt == '--no-focus-on-map':
  7073.             focus_on_map = False
  7074.  
  7075.         elif opt == '--debug':
  7076.             set_debugging (True)
  7077.  
  7078.     main(setup_printer, configure_printer, change_ppd, devid, print_test_page,
  7079.          focus_on_map)
  7080.